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