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