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