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