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 getInstance()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 CLDRConfig()146 protected CLDRConfig() { 147 initStack = StackTracker.currentStack(); 148 } 149 getInitStack()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 setTestLog(TestLog log)192 public TestLog setTestLog(TestLog log) { 193 testLog = log; 194 return log; 195 } 196 197 // for calling "run" setTestLog(TestFmwk log)198 public TestFmwk setTestLog(TestFmwk log) { 199 testLog = log; 200 return log; 201 } 202 logln(String msg)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 getSupplementalDataInfo()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 getStandardCodes()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 getCoverageInfo()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 getCldrFactory()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 getExemplarsFactory()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 getCollationFactory()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 getRBNFFactory()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 getAnnotationsFactory()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 getSubdivisionFactory()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 getMainAndAnnotationsFactory()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 getCommonSeedExemplarsFactory()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 getCommonAndSeedAndMainAndAnnotationsFactory()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 getFullCldrFactory()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 getSupplementalFactory()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 getEnglish()349 public CLDRFile getEnglish() { 350 return getCLDRFile("en", true); 351 } 352 getCLDRFile(String locale, boolean resolved)353 public CLDRFile getCLDRFile(String locale, boolean resolved) { 354 355 return resolved ? cldrFileResolvedCache.getUnchecked(locale) : cldrFileUnresolvedCache.getUnchecked(locale); 356 357 } 358 getRoot()359 public CLDRFile getRoot() { 360 return getCLDRFile("root", true); 361 } 362 getCollatorRoot()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 getCollator()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 getPhase()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 getProperty(String key, String d)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 get(Object key)417 public String get(Object key) { 418 return getProperty(key.toString()); 419 } 420 421 @Override getProperty(String key)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 getEnvironment()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 */ getDefaultEnvironment()465 protected Environment getDefaultEnvironment() { 466 return Environment.LOCAL; 467 } 468 setEnvironment(Environment environment)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 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<String, String>(); 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 * @param k key 499 * @param defVal default value 500 * @return 501 */ getProperty(String k, boolean defVal)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 */ getProperty(String k, int defVal)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 getCldrBaseDirectory()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 */ getAllCLDRFilesEndingWith(final String suffix)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 */ getCLDRFilesMatching(FilenameFilter filter, final File baseDir)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 */ getCLDRDataDirectories()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 */ getCLDRDataDirectories(String list)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 */ addStandardSubdirectories(String... base)625 public File[] addStandardSubdirectories(String... base) { 626 return addStandardSubdirectories(fileArrayFromStringArray(getCldrBaseDirectory(), base)); 627 } 628 addStandardSubdirectories(File... base)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 fileArrayFromStringArray(File dir, String... subdirNames)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 addIfExists(List<File> ret, File baseFile, String sub)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 */ getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo)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 */ isJunkFile(String name)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 */ getDebugSettingsFor(Class<?> callingClass)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 */ getDebugSettingsFor(Class<?> callingClass, boolean defaultValue)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 */ urls()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 */ absoluteUrls()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 */ internalGetAbsoluteUrls()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 */ internalGetUrls()756 protected CLDRURLS internalGetUrls() { 757 return absoluteUrls(); 758 } 759 760 private CLDRURLS urls = null; 761 private CLDRURLS absoluteUrls = null; 762 isCldrVersionBefore(int... version)763 public boolean isCldrVersionBefore(int... version) { 764 return getEnglish().getDtdVersionInfo() 765 .compareTo(getVersion(version)) < 0; 766 } 767 getVersion(int... versionInput)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