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("CLDR_DIR"), 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 } 206 getCollationFactory()207 public final Factory getCollationFactory() { 208 return CollationFactoryHelper.SINGLETON; 209 } 210 211 private static final class RBNFFactoryHelper { 212 static final Factory SINGLETON = Factory.make(CLDRPaths.RBNF_DIRECTORY, ".*"); 213 } 214 getRBNFFactory()215 public final Factory getRBNFFactory() { 216 return RBNFFactoryHelper.SINGLETON; 217 } 218 219 private static final class AnnotationsFactoryHelper { 220 static final Factory SINGLETON = Factory.make(CLDRPaths.ANNOTATIONS_DIRECTORY, ".*"); 221 } 222 getAnnotationsFactory()223 public Factory getAnnotationsFactory() { 224 return AnnotationsFactoryHelper.SINGLETON; 225 } 226 227 private static final class SubdivisionsFactoryHelper { 228 static final Factory SINGLETON = Factory.make(CLDRPaths.SUBDIVISIONS_DIRECTORY, ".*"); 229 } 230 getSubdivisionFactory()231 public final Factory getSubdivisionFactory() { 232 return SubdivisionsFactoryHelper.SINGLETON; 233 } 234 235 private static final class MainAndAnnotationsFactoryHelper { 236 private static final File[] paths = { 237 new File(CLDRPaths.MAIN_DIRECTORY), 238 new File(CLDRPaths.ANNOTATIONS_DIRECTORY) }; 239 static final Factory SINGLETON = SimpleFactory.make(paths, ".*"); 240 } 241 getMainAndAnnotationsFactory()242 public final Factory getMainAndAnnotationsFactory() { 243 return MainAndAnnotationsFactoryHelper.SINGLETON; 244 } 245 246 private static final class CommonSeedExemplarsFactoryHelper { 247 static final Factory SINGLETON = SimpleFactory.make(getInstance().addStandardSubdirectories(CLDR_DATA_DIRECTORIES), ".*"); 248 } 249 getCommonSeedExemplarsFactory()250 public final Factory getCommonSeedExemplarsFactory() { 251 return CommonSeedExemplarsFactoryHelper.SINGLETON; 252 } 253 254 private static final class CommonAndSeedAndMainAndAnnotationsFactoryHelper { 255 private static final File[] paths = { 256 new File(CLDRPaths.MAIN_DIRECTORY), 257 new File(CLDRPaths.ANNOTATIONS_DIRECTORY), 258 SKIP_SEED ? null : new File(CLDRPaths.SEED_DIRECTORY), 259 SKIP_SEED ? null : new File(CLDRPaths.SEED_ANNOTATIONS_DIRECTORY) 260 }; 261 static final Factory SINGLETON = SimpleFactory.make(paths, ".*"); 262 } 263 getCommonAndSeedAndMainAndAnnotationsFactory()264 public final Factory getCommonAndSeedAndMainAndAnnotationsFactory() { 265 return CommonAndSeedAndMainAndAnnotationsFactoryHelper.SINGLETON; 266 } 267 268 private static final class FullCldrFactoryHelper { 269 private static final File[] paths = { 270 new File(CLDRPaths.MAIN_DIRECTORY), 271 SKIP_SEED ? null : new File(CLDRPaths.SEED_DIRECTORY)}; 272 static final Factory SINGLETON = SimpleFactory.make(paths, ".*"); 273 } 274 getFullCldrFactory()275 public final Factory getFullCldrFactory() { 276 return FullCldrFactoryHelper.SINGLETON; 277 } 278 279 private static final class SupplementalFactoryHelper { 280 static final Factory SINGLETON = Factory.make(CLDRPaths.DEFAULT_SUPPLEMENTAL_DIRECTORY, ".*"); 281 } 282 getSupplementalFactory()283 public final Factory getSupplementalFactory() { 284 return SupplementalFactoryHelper.SINGLETON; 285 } 286 getEnglish()287 public CLDRFile getEnglish() { 288 return getCLDRFile("en", true); 289 } 290 getCLDRFile(String locale, boolean resolved)291 public CLDRFile getCLDRFile(String locale, boolean resolved) { 292 return resolved ? cldrFileResolvedCache.getUnchecked(locale) : cldrFileUnresolvedCache.getUnchecked(locale); 293 } 294 getRoot()295 public CLDRFile getRoot() { 296 return getCLDRFile("root", true); 297 } 298 299 private static final class CollatorRootHelper { 300 static final RuleBasedCollator SINGLETON = make(); 301 make()302 private static final RuleBasedCollator make() { 303 RuleBasedCollator colRoot; 304 305 CLDRFile root = getInstance().getCollationFactory().make("root", false); 306 String rules = root.getStringValue("//ldml/collations/collation[@type=\"emoji\"][@visibility=\"external\"]/cr"); 307 try { 308 colRoot = new RuleBasedCollator(rules); 309 } catch (Exception e) { 310 colRoot = (RuleBasedCollator) getInstance().getCollator(); 311 return colRoot; 312 } 313 colRoot.setStrength(Collator.IDENTICAL); 314 colRoot.setNumericCollation(true); 315 colRoot.freeze(); 316 return colRoot; 317 } 318 } getCollatorRoot()319 public final Collator getCollatorRoot() { 320 return CollatorRootHelper.SINGLETON; 321 } 322 323 @SuppressWarnings("unchecked") getComparatorRoot()324 public final Comparator<String> getComparatorRoot() { 325 return (Comparator)(getCollatorRoot()); 326 } 327 328 private static final class CollatorHelper { 329 static final Collator EMOJI_COLLATOR = makeEmojiCollator(); makeEmojiCollator()330 private static final Collator makeEmojiCollator() { 331 final RuleBasedCollator col = (RuleBasedCollator) Collator.getInstance(ULocale.forLanguageTag("en-u-co-emoji")); 332 col.setStrength(Collator.IDENTICAL); 333 col.setNumericCollation(true); 334 col.freeze(); 335 return col; 336 } 337 338 static final Collator ROOT_NUMERIC = makeRootNumeric(); 339 makeRootNumeric()340 private static final Collator makeRootNumeric() { 341 RuleBasedCollator _ROOT_COL = (RuleBasedCollator) Collator.getInstance(ULocale.ENGLISH); 342 _ROOT_COL.setNumericCollation(true); 343 _ROOT_COL.freeze(); 344 return _ROOT_COL; 345 } 346 } getCollator()347 public Collator getCollator() { 348 return CollatorHelper.EMOJI_COLLATOR; 349 } 350 getRootNumeric()351 public Collator getRootNumeric() { 352 return CollatorHelper.ROOT_NUMERIC; 353 } 354 getPhase()355 public synchronized Phase getPhase() { 356 if (phase == null) { 357 if (getEnvironment() == Environment.UNITTEST) { 358 phase = Phase.BUILD; 359 } else { 360 phase = Phase.SUBMISSION; 361 } 362 } 363 return phase; 364 } 365 366 @Override getProperty(String key, String d)367 public String getProperty(String key, String d) { 368 String result = getProperty(key); 369 if (result == null) return d; 370 return result; 371 } 372 373 private Set<String> shown = new HashSet<>(); 374 375 private Map<String, String> localSet = null; 376 377 @Override get(Object key)378 public String get(Object key) { 379 return getProperty(key.toString()); 380 } 381 382 @Override getProperty(String key)383 public String getProperty(String key) { 384 String result = null; 385 if (localSet != null) { 386 result = localSet.get(key); 387 } 388 if (result == null) { 389 result = System.getProperty(key); 390 } 391 if (result == null) { 392 result = System.getProperty(key.toUpperCase(Locale.ENGLISH)); 393 } 394 if (result == null) { 395 result = System.getProperty(key.toLowerCase(Locale.ENGLISH)); 396 } 397 if (result == null) { 398 result = System.getenv(key); 399 } 400 if (DEBUG && !shown.contains(key)) { 401 logln("-D" + key + "=" + result); 402 shown.add(key); 403 } 404 return result; 405 } 406 407 private Environment curEnvironment = null; 408 getEnvironment()409 public Environment getEnvironment() { 410 if (curEnvironment == null) { 411 String envString = getProperty("CLDR_ENVIRONMENT"); 412 if (envString != null) { 413 curEnvironment = Environment.valueOf(envString.trim()); 414 } 415 if (curEnvironment == null) { 416 curEnvironment = getDefaultEnvironment(); 417 } 418 } 419 return curEnvironment; 420 } 421 422 /** 423 * If no environment is defined, what is the default? 424 * @return 425 */ getDefaultEnvironment()426 protected Environment getDefaultEnvironment() { 427 return Environment.LOCAL; 428 } 429 setEnvironment(Environment environment)430 public void setEnvironment(Environment environment) { 431 curEnvironment = environment; 432 } 433 434 /** 435 * For test use only. Will throw an exception in non test environments. 436 * @param k 437 * @param v 438 * @return 439 */ 440 @Override setProperty(String k, String v)441 public Object setProperty(String k, String v) { 442 if (getEnvironment() != Environment.UNITTEST) { 443 throw new InternalError("setProperty() only valid in UNITTEST Environment."); 444 } 445 if (localSet == null) { 446 localSet = new ConcurrentHashMap<>(); 447 } 448 shown.remove(k); // show it again with -D 449 return localSet.put(k, v); 450 } 451 452 @Override put(Object k, Object v)453 public Object put(Object k, Object v) { 454 return setProperty(k.toString(), v.toString()); 455 } 456 457 /** 458 * Return true if the value indicates 'true' 459 * @param k key 460 * @param defVal default value 461 * @return 462 */ getProperty(String k, boolean defVal)463 public boolean getProperty(String k, boolean defVal) { 464 String val = getProperty(k, defVal ? "true" : null); 465 if (val == null) { 466 return false; 467 } else { 468 val = val.trim().toLowerCase(); 469 return (val.equals("true") || val.equals("t") || val.equals("yes") || val.equals("y")); 470 } 471 } 472 473 /** 474 * Return a numeric property 475 * @param k key 476 * @param defVal default value 477 * @return 478 */ getProperty(String k, int defVal)479 public int getProperty(String k, int defVal) { 480 String val = getProperty(k, Integer.toString(defVal)); 481 if (val == null) { 482 return defVal; 483 } else { 484 try { 485 return Integer.parseInt(val); 486 } catch (NumberFormatException nfe) { 487 return defVal; 488 } 489 } 490 } 491 492 private static class FileWrapper { 493 private File cldrDir = null; FileWrapper()494 private FileWrapper() { 495 String dir = getInstance().getProperty("CLDR_DIR", null); 496 if (dir != null) { 497 cldrDir = new File(dir); 498 } else { 499 cldrDir = null; 500 } 501 } getCldrDir()502 public File getCldrDir() { 503 return this.cldrDir; 504 } 505 // singleton 506 private static FileWrapper fileWrapperInstance = new FileWrapper(); getFileWrapperInstance()507 public static FileWrapper getFileWrapperInstance() { 508 return fileWrapperInstance; 509 } 510 } 511 getCldrBaseDirectory()512 public File getCldrBaseDirectory() { 513 return FileWrapper.getFileWrapperInstance().getCldrDir(); 514 } 515 516 /** 517 * Get all CLDR XML files in the CLDR base directory. 518 * @return 519 */ getAllCLDRFilesEndingWith(final String suffix)520 public Set<File> getAllCLDRFilesEndingWith(final String suffix) { 521 FilenameFilter filter = new FilenameFilter() { 522 @Override 523 public boolean accept(File dir, String name) { 524 return name.endsWith(suffix) && !isJunkFile(name); // skip junk and backup files 525 } 526 }; 527 final File dir = getCldrBaseDirectory(); 528 Set<File> list; 529 list = getCLDRFilesMatching(filter, dir); 530 return list; 531 } 532 533 /** 534 * Return all CLDR data files matching this filter 535 * @param filter matching filter 536 * @param baseDir base directory, see {@link #getCldrBaseDirectory()} 537 * @return set of files 538 */ getCLDRFilesMatching(FilenameFilter filter, final File baseDir)539 public Set<File> getCLDRFilesMatching(FilenameFilter filter, final File baseDir) { 540 Set<File> list; 541 list = new LinkedHashSet<>(); 542 for (String subdir : getCLDRDataDirectories()) { 543 getFilesRecursively(new File(baseDir, subdir), filter, list); 544 } 545 return list; 546 } 547 548 /** 549 * TODO: better place for these constants? 550 */ 551 private static final String COMMON_DIR = "common"; 552 /** 553 * TODO: better place for these constants? 554 */ 555 private static final String EXEMPLARS_DIR = "exemplars"; 556 /** 557 * TODO: better place for these constants? 558 */ 559 private static final String SEED_DIR = "seed"; 560 /** 561 * TODO: better place for these constants? 562 */ 563 private static final String KEYBOARDS_DIR = "keyboards"; 564 private static final String MAIN_DIR = "main"; 565 private static final String ANNOTATIONS_DIR = "annotations"; 566 private static final String SUBDIVISIONS_DIR = "subdivisions"; 567 568 /** 569 * TODO: better place for these constants? 570 */ 571 private static final String CLDR_DATA_DIRECTORIES[] = { COMMON_DIR, SEED_DIR, KEYBOARDS_DIR, EXEMPLARS_DIR }; 572 private static final ImmutableSet<String> STANDARD_SUBDIRS = ImmutableSet.of(MAIN_DIR, ANNOTATIONS_DIR, SUBDIVISIONS_DIR); 573 574 /** 575 * Get a list of CLDR directories containing actual data 576 * @return an iterable containing the names of all CLDR data subdirectories 577 */ getCLDRDataDirectories()578 public static Iterable<String> getCLDRDataDirectories() { 579 return Arrays.asList(CLDR_DATA_DIRECTORIES); 580 } 581 582 /** 583 * Given comma separated list "common" or "common,main" return a list of actual files. 584 * Adds subdirectories in STANDARD_SUBDIRS as necessary. 585 */ getCLDRDataDirectories(String list)586 public File[] getCLDRDataDirectories(String list) { 587 final File dir = getCldrBaseDirectory(); 588 String stubs[] = list.split(","); 589 File[] ret = new File[stubs.length]; 590 for (int i = 0; i < stubs.length; i++) { 591 ret[i] = new File(dir, stubs[i]); 592 } 593 return ret; 594 } 595 596 /** 597 * Add subdirectories to file list as needed, from STANDARD_SUBDIRS. 598 * <ul><li>map "common","seed" -> "common/main", "seed/main" 599 * <li>but common/main -> common/main 600 * </ul> 601 */ addStandardSubdirectories(String... base)602 public File[] addStandardSubdirectories(String... base) { 603 return addStandardSubdirectories(fileArrayFromStringArray(getCldrBaseDirectory(), base)); 604 } 605 addStandardSubdirectories(File... base)606 public File[] addStandardSubdirectories(File... base) { 607 List<File> ret = new ArrayList<>(); 608 //File[] ret = new File[base.length * 2]; 609 for (int i = 0; i < base.length; i++) { 610 File baseFile = base[i]; 611 String name = baseFile.getName(); 612 if (STANDARD_SUBDIRS.contains(name)) { 613 ret.add(baseFile); 614 } else { 615 for (String sub : STANDARD_SUBDIRS) { 616 addIfExists(ret, baseFile, sub); 617 } 618 } 619 } 620 return ret.toArray(new File[ret.size()]); 621 } 622 fileArrayFromStringArray(File dir, String... subdirNames)623 public static File[] fileArrayFromStringArray(File dir, String... subdirNames) { 624 File[] fileList = new File[subdirNames.length]; 625 int i = 0; 626 for (String item : subdirNames) { 627 fileList[i++] = new File(dir, item); 628 } 629 return fileList; 630 } 631 addIfExists(List<File> ret, File baseFile, String sub)632 private static void addIfExists(List<File> ret, File baseFile, String sub) { 633 File file = new File(baseFile, sub); 634 if (file.exists()) { 635 ret.add(file); 636 } 637 } 638 639 /** 640 * Utility function. Recursively add to a list of files. Skips ".svn" and junk directories. 641 * @param directory base directory 642 * @param filter filter to restrict files added 643 * @param toAddTo set to add to 644 * @return returns toAddTo. 645 */ getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo)646 public Set<File> getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo) { 647 File files[] = directory.listFiles(); 648 if (files != null) { 649 for (File subfile : files) { 650 if (subfile.isDirectory()) { 651 if (!isJunkFile(subfile.getName())) { 652 getFilesRecursively(subfile, filter, toAddTo); 653 } 654 } else if (filter.accept(directory, subfile.getName())) { 655 toAddTo.add(subfile); 656 } 657 } 658 } 659 return toAddTo; 660 } 661 662 /** 663 * Is the filename junk? (subversion, backup, etc) 664 * @param name 665 * @return 666 */ isJunkFile(String name)667 public static final boolean isJunkFile(String name) { 668 return name.startsWith(".") || (name.startsWith("#")); // Skip: .svn, .BACKUP, #backup# files. 669 } 670 671 /** 672 * Get the value of the debug setting for the calling class; assuming that no debugging is wanted if the property 673 * value cannot be found 674 * @param callingClass 675 * @return 676 * @see {@link #getDebugSettingsFor(Class, boolean)} 677 */ getDebugSettingsFor(Class<?> callingClass)678 public boolean getDebugSettingsFor(Class<?> callingClass) { 679 return getDebugSettingsFor(callingClass, false); 680 } 681 682 /** 683 * Get the debug settings (whether debugging is enabled for the calling class; This will look for a property corresponding 684 * to the canonical classname +".debug"; if that property cannot be found, the default value will be returned. 685 * @param callingClass 686 * @param defaultValue 687 * @return 688 */ getDebugSettingsFor(Class<?> callingClass, boolean defaultValue)689 public boolean getDebugSettingsFor(Class<?> callingClass, boolean defaultValue) { 690 // avoid NPE 691 if (callingClass == null) { 692 return defaultValue; 693 } 694 return getProperty(callingClass.getCanonicalName() + ".debug", defaultValue); 695 } 696 697 /** 698 * Get the URL generator for "general purpose" (non chart) use. 699 * @return 700 */ urls()701 public CLDRURLS urls() { 702 if (urls == null) { 703 synchronized (this) { 704 urls = internalGetUrls(); 705 } 706 } 707 return urls; 708 } 709 710 /** 711 * Get the URL generator for "absolute" (chart, email) use. 712 * By default, this is the same as urls. 713 */ absoluteUrls()714 public CLDRURLS absoluteUrls() { 715 if (absoluteUrls == null) { 716 synchronized (this) { 717 absoluteUrls = internalGetAbsoluteUrls(); 718 } 719 } 720 return absoluteUrls; 721 } 722 723 /** 724 * Probably would not need to override this. 725 */ internalGetAbsoluteUrls()726 protected CLDRURLS internalGetAbsoluteUrls() { 727 return new StaticCLDRURLS(this.getProperty(CLDRURLS.CLDR_SURVEY_BASE, CLDRURLS.DEFAULT_BASE)); 728 } 729 730 /** 731 * Override this to provide a different URL source for non-absolute URLs. 732 */ internalGetUrls()733 protected CLDRURLS internalGetUrls() { 734 return absoluteUrls(); 735 } 736 737 private CLDRURLS urls = null; 738 private CLDRURLS absoluteUrls = null; 739 isCldrVersionBefore(int... version)740 public boolean isCldrVersionBefore(int... version) { 741 return getEnglish().getDtdVersionInfo() 742 .compareTo(getVersion(version)) < 0; 743 } 744 getVersion(int... versionInput)745 public static VersionInfo getVersion(int... versionInput) { 746 int[] version = new int[4]; 747 for (int i = 0; i < versionInput.length; ++i) { 748 version[i] = versionInput[i]; 749 } 750 return VersionInfo.getInstance(version[0], version[1], version[2], 751 version[3]); 752 } 753 } 754