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 @Override getProperty(String key, String d)404 public String getProperty(String key, String d) { 405 String result = getProperty(key); 406 if (result == null) return d; 407 return result; 408 } 409 410 private Set<String> shown = new HashSet<>(); 411 412 private Map<String, String> localSet = null; 413 414 @Override get(Object key)415 public String get(Object key) { 416 return getProperty(key.toString()); 417 } 418 419 @Override getProperty(String key)420 public String getProperty(String key) { 421 String result = null; 422 if (localSet != null) { 423 result = localSet.get(key); 424 } 425 if (result == null) { 426 result = System.getProperty(key); 427 } 428 if (result == null) { 429 result = System.getProperty(key.toUpperCase(Locale.ENGLISH)); 430 } 431 if (result == null) { 432 result = System.getProperty(key.toLowerCase(Locale.ENGLISH)); 433 } 434 if (result == null) { 435 result = System.getenv(key); 436 } 437 if (DEBUG && !shown.contains(key)) { 438 logln("-D" + key + "=" + result); 439 shown.add(key); 440 } 441 return result; 442 } 443 444 private Environment curEnvironment = null; 445 getEnvironment()446 public Environment getEnvironment() { 447 if (curEnvironment == null) { 448 String envString = getProperty("CLDR_ENVIRONMENT"); 449 if (envString != null) { 450 curEnvironment = Environment.valueOf(envString.trim()); 451 } 452 if (curEnvironment == null) { 453 curEnvironment = getDefaultEnvironment(); 454 } 455 } 456 return curEnvironment; 457 } 458 459 /** 460 * If no environment is defined, what is the default? 461 * 462 * @return 463 */ getDefaultEnvironment()464 protected Environment getDefaultEnvironment() { 465 return Environment.LOCAL; 466 } 467 setEnvironment(Environment environment)468 public void setEnvironment(Environment environment) { 469 curEnvironment = environment; 470 } 471 472 /** 473 * For test use only. Will throw an exception in non test environments. 474 * 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<>(); 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 * 499 * @param k key 500 * @param defVal default value 501 * @return 502 */ getProperty(String k, boolean defVal)503 public boolean getProperty(String k, boolean defVal) { 504 String val = getProperty(k, defVal ? "true" : null); 505 if (val == null) { 506 return false; 507 } else { 508 val = val.trim().toLowerCase(); 509 return (val.equals("true") || val.equals("t") || val.equals("yes") || val.equals("y")); 510 } 511 } 512 513 /** 514 * Return a numeric property 515 * 516 * @param k key 517 * @param defVal default value 518 * @return 519 */ getProperty(String k, int defVal)520 public int getProperty(String k, int defVal) { 521 String val = getProperty(k, Integer.toString(defVal)); 522 if (val == null) { 523 return defVal; 524 } else { 525 try { 526 return Integer.parseInt(val); 527 } catch (NumberFormatException nfe) { 528 return defVal; 529 } 530 } 531 } 532 533 private static class FileWrapper { 534 private File cldrDir = null; 535 FileWrapper()536 private FileWrapper() { 537 String dir = getInstance().getProperty(CldrUtility.DIR_KEY, null); 538 if (dir != null) { 539 cldrDir = new File(dir); 540 } else { 541 cldrDir = null; 542 } 543 } 544 getCldrDir()545 public File getCldrDir() { 546 return this.cldrDir; 547 } 548 // singleton 549 private static FileWrapper fileWrapperInstance = new FileWrapper(); 550 getFileWrapperInstance()551 public static FileWrapper getFileWrapperInstance() { 552 return fileWrapperInstance; 553 } 554 } 555 getCldrBaseDirectory()556 public File getCldrBaseDirectory() { 557 return FileWrapper.getFileWrapperInstance().getCldrDir(); 558 } 559 560 /** 561 * Get all CLDR XML files in the CLDR base directory. 562 * 563 * @return 564 */ getAllCLDRFilesEndingWith(final String suffix)565 public Set<File> getAllCLDRFilesEndingWith(final String suffix) { 566 FilenameFilter filter = 567 new FilenameFilter() { 568 @Override 569 public boolean accept(File dir, String name) { 570 return name.endsWith(suffix) 571 && !isJunkFile(name); // skip junk and backup files 572 } 573 }; 574 final File dir = getCldrBaseDirectory(); 575 Set<File> list; 576 list = getCLDRFilesMatching(filter, dir); 577 return list; 578 } 579 580 /** 581 * Return all CLDR data files matching this filter 582 * 583 * @param filter matching filter 584 * @param baseDir base directory, see {@link #getCldrBaseDirectory()} 585 * @return set of files 586 */ getCLDRFilesMatching(FilenameFilter filter, final File baseDir)587 public Set<File> getCLDRFilesMatching(FilenameFilter filter, final File baseDir) { 588 Set<File> list; 589 list = new LinkedHashSet<>(); 590 for (String subdir : getCLDRDataDirectories()) { 591 getFilesRecursively(new File(baseDir, subdir), filter, list); 592 } 593 return list; 594 } 595 596 /** TODO: better place for these constants? */ 597 private static final String COMMON_DIR = "common"; 598 /** TODO: better place for these constants? */ 599 private static final String EXEMPLARS_DIR = "exemplars"; 600 /** TODO: better place for these constants? */ 601 private static final String SEED_DIR = "seed"; 602 /** TODO: better place for these constants? */ 603 private static final String KEYBOARDS_DIR = "keyboards"; 604 605 private static final String MAIN_DIR = "main"; 606 private static final String ANNOTATIONS_DIR = "annotations"; 607 private static final String SUBDIVISIONS_DIR = "subdivisions"; 608 609 /** TODO: better place for these constants? */ 610 private static final String CLDR_DATA_DIRECTORIES[] = { 611 COMMON_DIR, SEED_DIR, KEYBOARDS_DIR, EXEMPLARS_DIR 612 }; 613 614 private static final ImmutableSet<String> STANDARD_SUBDIRS = 615 ImmutableSet.of(MAIN_DIR, ANNOTATIONS_DIR, SUBDIVISIONS_DIR); 616 617 /** 618 * Get a list of CLDR directories containing actual data 619 * 620 * @return an iterable containing the names of all CLDR data subdirectories 621 */ getCLDRDataDirectories()622 public static Iterable<String> getCLDRDataDirectories() { 623 return Arrays.asList(CLDR_DATA_DIRECTORIES); 624 } 625 626 /** 627 * Given comma separated list "common" or "common,main" return a list of actual files. Adds 628 * subdirectories in STANDARD_SUBDIRS as necessary. 629 */ getCLDRDataDirectories(String list)630 public File[] getCLDRDataDirectories(String list) { 631 final File dir = getCldrBaseDirectory(); 632 String stubs[] = list.split(","); 633 File[] ret = new File[stubs.length]; 634 for (int i = 0; i < stubs.length; i++) { 635 ret[i] = new File(dir, stubs[i]); 636 } 637 return ret; 638 } 639 640 /** 641 * Add subdirectories to file list as needed, from STANDARD_SUBDIRS. 642 * 643 * <ul> 644 * <li>map "common","seed" -> "common/main", "seed/main" 645 * <li>but common/main -> common/main 646 * </ul> 647 */ addStandardSubdirectories(String... base)648 public File[] addStandardSubdirectories(String... base) { 649 return addStandardSubdirectories(fileArrayFromStringArray(getCldrBaseDirectory(), base)); 650 } 651 addStandardSubdirectories(File... base)652 public File[] addStandardSubdirectories(File... base) { 653 List<File> ret = new ArrayList<>(); 654 // File[] ret = new File[base.length * 2]; 655 for (int i = 0; i < base.length; i++) { 656 File baseFile = base[i]; 657 String name = baseFile.getName(); 658 if (STANDARD_SUBDIRS.contains(name)) { 659 ret.add(baseFile); 660 } else { 661 for (String sub : STANDARD_SUBDIRS) { 662 addIfExists(ret, baseFile, sub); 663 } 664 } 665 } 666 return ret.toArray(new File[ret.size()]); 667 } 668 fileArrayFromStringArray(File dir, String... subdirNames)669 public static File[] fileArrayFromStringArray(File dir, String... subdirNames) { 670 File[] fileList = new File[subdirNames.length]; 671 int i = 0; 672 for (String item : subdirNames) { 673 fileList[i++] = new File(dir, item); 674 } 675 return fileList; 676 } 677 addIfExists(List<File> ret, File baseFile, String sub)678 private static void addIfExists(List<File> ret, File baseFile, String sub) { 679 File file = new File(baseFile, sub); 680 if (file.exists()) { 681 ret.add(file); 682 } 683 } 684 685 /** 686 * Utility function. Recursively add to a list of files. Skips ".svn" and junk directories. 687 * 688 * @param directory base directory 689 * @param filter filter to restrict files added 690 * @param toAddTo set to add to 691 * @return returns toAddTo. 692 */ getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo)693 public Set<File> getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo) { 694 File files[] = directory.listFiles(); 695 if (files != null) { 696 for (File subfile : files) { 697 if (subfile.isDirectory()) { 698 if (!isJunkFile(subfile.getName())) { 699 getFilesRecursively(subfile, filter, toAddTo); 700 } 701 } else if (filter.accept(directory, subfile.getName())) { 702 toAddTo.add(subfile); 703 } 704 } 705 } 706 return toAddTo; 707 } 708 709 /** 710 * Is the filename junk? (subversion, backup, etc) 711 * 712 * @param name 713 * @return 714 */ isJunkFile(String name)715 public static final boolean isJunkFile(String name) { 716 return name.startsWith(".") 717 || (name.startsWith("#")); // Skip: .svn, .BACKUP, #backup# files. 718 } 719 720 /** 721 * Get the value of the debug setting for the calling class; assuming that no debugging is 722 * wanted if the property value cannot be found 723 * 724 * @param callingClass 725 * @return 726 * @see {@link #getDebugSettingsFor(Class, boolean)} 727 */ getDebugSettingsFor(Class<?> callingClass)728 public boolean getDebugSettingsFor(Class<?> callingClass) { 729 return getDebugSettingsFor(callingClass, false); 730 } 731 732 /** 733 * Get the debug settings (whether debugging is enabled for the calling class; This will look 734 * for a property corresponding to the canonical classname +".debug"; if that property cannot be 735 * found, the default value will be returned. 736 * 737 * @param callingClass 738 * @param defaultValue 739 * @return 740 */ getDebugSettingsFor(Class<?> callingClass, boolean defaultValue)741 public boolean getDebugSettingsFor(Class<?> callingClass, boolean defaultValue) { 742 // avoid NPE 743 if (callingClass == null) { 744 return defaultValue; 745 } 746 return getProperty(callingClass.getCanonicalName() + ".debug", defaultValue); 747 } 748 749 /** 750 * Get the URL generator for "general purpose" (non chart) use. 751 * 752 * @return 753 */ urls()754 public CLDRURLS urls() { 755 if (urls == null) { 756 synchronized (this) { 757 urls = internalGetUrls(); 758 } 759 } 760 return urls; 761 } 762 763 /** 764 * Get the URL generator for "absolute" (chart, email) use. By default, this is the same as 765 * urls. 766 */ absoluteUrls()767 public CLDRURLS absoluteUrls() { 768 if (absoluteUrls == null) { 769 synchronized (this) { 770 absoluteUrls = internalGetAbsoluteUrls(); 771 } 772 } 773 return absoluteUrls; 774 } 775 776 /** Probably would not need to override this. */ internalGetAbsoluteUrls()777 protected CLDRURLS internalGetAbsoluteUrls() { 778 return new StaticCLDRURLS( 779 this.getProperty(CLDRURLS.CLDR_SURVEY_BASE, CLDRURLS.DEFAULT_BASE)); 780 } 781 782 /** Override this to provide a different URL source for non-absolute URLs. */ internalGetUrls()783 protected CLDRURLS internalGetUrls() { 784 return absoluteUrls(); 785 } 786 787 private CLDRURLS urls = null; 788 private CLDRURLS absoluteUrls = null; 789 isCldrVersionBefore(int... version)790 public boolean isCldrVersionBefore(int... version) { 791 return getEnglish().getDtdVersionInfo().compareTo(getVersion(version)) < 0; 792 } 793 getVersion(int... versionInput)794 public static VersionInfo getVersion(int... versionInput) { 795 int[] version = new int[4]; 796 for (int i = 0; i < versionInput.length; ++i) { 797 version[i] = versionInput[i]; 798 } 799 return VersionInfo.getInstance(version[0], version[1], version[2], version[3]); 800 } 801 } 802