1 package org.unicode.cldr.util; 2 3 import java.io.File; 4 import java.io.InputStream; 5 import java.util.ArrayList; 6 import java.util.Arrays; 7 import java.util.Collections; 8 import java.util.Iterator; 9 import java.util.List; 10 import java.util.Map; 11 import java.util.Objects; 12 import java.util.Set; 13 import java.util.TreeSet; 14 import java.util.regex.Matcher; 15 16 import org.unicode.cldr.util.CLDRFile.DraftStatus; 17 import org.unicode.cldr.util.XMLSource.ResolvingSource; 18 19 import com.google.common.cache.Cache; 20 import com.google.common.cache.CacheBuilder; 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.ImmutableList.Builder; 23 import com.google.common.collect.ImmutableSet; 24 import com.ibm.icu.util.ICUException; 25 import com.ibm.icu.util.ICUUncheckedIOException; 26 27 public class SimpleFactory extends Factory { 28 29 /** 30 * Variable to control the behaviour of the class. 31 * TRUE - use a (non-static) array of Maps, indexed by locale String (old behaviour) 32 * FALSE - use a single static map, indexed with a more elaborate key. 33 */ 34 private static final boolean USE_OLD_HANDLEMAKE_CODE = false; 35 36 /** 37 * Variable that customizes the caching of the results of SimpleFactory.make 38 * 39 */ 40 private static final boolean CACHE_SIMPLE_FACTORIES = false; 41 42 /** 43 * Number of Factories that should be cached, if caching of factories is enabled 44 */ 45 private static final int FACTORY_CACHE_LIMIT = 10; 46 47 /** 48 * Object that is used for synchronization when looking up simple factories 49 */ 50 private static final Object FACTORY_LOOKUP_SYNC = new Object(); 51 /** 52 * The maximum cache size the caches in 53 * 15 is a safe limit for instances with limited amounts of memory (around 128MB). 54 * Larger numbers are tolerable if more memory is available. 55 * This constant may be moved to CldrUtilities in future if needed. 56 */ 57 // private static final int CACHE_LIMIT = 15; 58 59 private static final int CACHE_LIMIT = 75; 60 61 private static final boolean DEBUG_SIMPLEFACTORY = false; 62 63 /** 64 * Simple class used as a key for the map that holds the CLDRFiles -only used in the new version of the code 65 * @author ribnitz 66 * 67 */ 68 private static class CLDRCacheKey { 69 private final String localeName; 70 private final boolean resolved; 71 private final DraftStatus draftStatus; 72 private final Set<String> directories; // ordered 73 private final int hashCode; 74 CLDRCacheKey(String localeName, boolean resolved, DraftStatus draftStatus, List<File> directories)75 public CLDRCacheKey(String localeName, boolean resolved, DraftStatus draftStatus, List<File> directories) { 76 super(); 77 this.localeName = localeName; 78 this.resolved = resolved; 79 this.draftStatus = draftStatus; 80 // Parameter check: the directory/file supplied must be non-null and readable. 81 if (directories == null || directories.isEmpty()) { 82 throw new ICUUncheckedIOException("Attempt to create a CLDRCacheKey with a null directory, please supply a non-null one."); 83 } 84 ImmutableSet.Builder<String> _directories = ImmutableSet.builder(); 85 for (File directory : directories) { 86 if (!directory.canRead()) { 87 throw new ICUUncheckedIOException("The directory specified, " + directory.getPath() + ", cannot be read"); 88 } 89 _directories.add(directory.toString()); 90 } 91 this.directories = _directories.build(); 92 hashCode = Objects.hash(this.localeName, this.resolved, this.draftStatus, this.directories); 93 } 94 95 @Override hashCode()96 public int hashCode() { 97 return hashCode; 98 } 99 100 @Override equals(Object obj)101 public boolean equals(Object obj) { 102 if (this == obj) { 103 return true; 104 } 105 if (obj == null) { 106 return false; 107 } 108 if (getClass() != obj.getClass()) { 109 return false; 110 } 111 CLDRCacheKey other = (CLDRCacheKey) obj; 112 if (!Objects.equals(directories, other.directories)) { 113 return false; 114 } 115 if (draftStatus != other.draftStatus) { 116 return false; 117 } 118 if (localeName == null) { 119 if (other.localeName != null) { 120 return false; 121 } 122 } else if (!localeName.equals(other.localeName)) { 123 return false; 124 } 125 if (resolved != other.resolved) { 126 return false; 127 } 128 return true; 129 } 130 toString()131 public String toString() { 132 return "[ LocaleName: " + localeName + " Resolved: " + resolved + " Draft status: " + draftStatus + " Directories: " + directories + " ]"; 133 } 134 } 135 136 /** 137 * If a SimpleDFactory covers more than one directory, SimpleFactoryLookupKey Objects may 138 * be needed to find the SimpleFactory that is responsible for the given directory 139 * @author ribnitz 140 * 141 */ 142 private static class SimpleFactoryLookupKey { 143 private final String directory; 144 private final String matchString; 145 SimpleFactoryLookupKey(String directory, String matchString)146 public SimpleFactoryLookupKey(String directory, String matchString) { 147 this.directory = directory; 148 this.matchString = matchString; 149 } 150 151 @Override hashCode()152 public int hashCode() { 153 final int prime = 31; 154 int result = 1; 155 result = prime * result + ((directory == null) ? 0 : directory.hashCode()); 156 result = prime * result + ((matchString == null) ? 0 : matchString.hashCode()); 157 return result; 158 } 159 160 @Override equals(Object obj)161 public boolean equals(Object obj) { 162 if (this == obj) { 163 return true; 164 } 165 if (obj == null) { 166 return false; 167 } 168 if (getClass() != obj.getClass()) { 169 return false; 170 } 171 SimpleFactoryLookupKey other = (SimpleFactoryLookupKey) obj; 172 if (directory == null) { 173 if (other.directory != null) { 174 return false; 175 } 176 } else if (!directory.equals(other.directory)) { 177 return false; 178 } 179 if (matchString == null) { 180 if (other.matchString != null) { 181 return false; 182 } 183 } else if (!matchString.equals(other.matchString)) { 184 return false; 185 } 186 return true; 187 } 188 getDirectory()189 public String getDirectory() { 190 return directory; 191 } 192 getMatchString()193 public String getMatchString() { 194 return matchString; 195 } 196 197 @Override toString()198 public String toString() { 199 return "SimpleFactoryLookupKey [directory=" + directory + ", matchString=" + matchString + "]"; 200 } 201 202 } 203 204 /** 205 * Simple class to use as a Key in a map that caches SimpleFacotry instances. 206 * @author ribnitz 207 * 208 */ 209 private static class SimpleFactoryCacheKey { 210 private List<String> sourceDirectories; 211 private String matchString; 212 private DraftStatus mimimalDraftStatus; 213 SimpleFactoryCacheKey(List<String> sourceDirectories, String matchString, DraftStatus mimimalDraftStatus)214 public SimpleFactoryCacheKey(List<String> sourceDirectories, String matchString, DraftStatus mimimalDraftStatus) { 215 this.sourceDirectories = sourceDirectories; 216 this.matchString = matchString; 217 this.mimimalDraftStatus = mimimalDraftStatus; 218 } 219 220 @Override hashCode()221 public int hashCode() { 222 final int prime = 31; 223 int result = 1; 224 result = prime * result + ((matchString == null) ? 0 : matchString.hashCode()); 225 result = prime * result + ((mimimalDraftStatus == null) ? 0 : mimimalDraftStatus.hashCode()); 226 result = prime * result + ((sourceDirectories == null) ? 0 : sourceDirectories.hashCode()); 227 return result; 228 } 229 230 @Override equals(Object obj)231 public boolean equals(Object obj) { 232 if (this == obj) { 233 return true; 234 } 235 if (obj == null) { 236 return false; 237 } 238 if (getClass() != obj.getClass()) { 239 return false; 240 } 241 SimpleFactoryCacheKey other = (SimpleFactoryCacheKey) obj; 242 if (matchString == null) { 243 if (other.matchString != null) { 244 return false; 245 } 246 } else if (!matchString.equals(other.matchString)) { 247 return false; 248 } 249 if (mimimalDraftStatus != other.mimimalDraftStatus) { 250 return false; 251 } 252 if (sourceDirectories == null) { 253 if (other.sourceDirectories != null) { 254 return false; 255 } 256 } else if (!sourceDirectories.equals(other.sourceDirectories)) { 257 return false; 258 } 259 return true; 260 } 261 getSourceDirectories()262 public List<String> getSourceDirectories() { 263 return sourceDirectories; 264 } 265 getMatchString()266 public String getMatchString() { 267 return matchString; 268 } 269 getMimimalDraftStatus()270 public DraftStatus getMimimalDraftStatus() { 271 return mimimalDraftStatus; 272 } 273 274 @Override toString()275 public String toString() { 276 StringBuilder builder = new StringBuilder(); 277 builder.append("SimpleFactoryCacheKey [sourceDirectories=").append(sourceDirectories).append(", matchString=").append(matchString) 278 .append(", mimimalDraftStatus=").append(mimimalDraftStatus).append("]"); 279 return builder.toString(); 280 } 281 282 } 283 284 // private volatile CLDRFile result; // used in handleMake 285 private File sourceDirectories[]; 286 private Set<String> localeList = new TreeSet<String>(); 287 private Cache<CLDRCacheKey, CLDRFile> combinedCache = null; 288 //private Map<CLDRCacheKey,CLDRFile> combinedCache= null; 289 // Collections.synchronizedMap(new LruMap<CLDRCacheKey, CLDRFile>(CACHE_LIMIT)); 290 291 private Map<String, CLDRFile>[] mainCache = null; /* new Map[DraftStatus.values().length]; */ 292 private Map<String, CLDRFile>[] resolvedCache = null; /*new Map[DraftStatus.values().length]; */ 293 // { 294 // for (int i = 0; i < mainCache.length; ++i) { 295 // mainCache[i] = Collections.synchronizedMap(new LruMap<String, CLDRFile>(CACHE_LIMIT)); 296 // resolvedCache[i] = Collections.synchronizedMap(new LruMap<String, CLDRFile>(CACHE_LIMIT)); 297 // } 298 // } 299 private DraftStatus minimalDraftStatus = DraftStatus.unconfirmed; 300 301 /* Use WeakValues - automagically remove a value once it is no longer useed elsewhere */ 302 private static Cache<SimpleFactoryCacheKey, SimpleFactory> factoryCache = null; 303 // private static LockSupportMap<SimpleFactoryCacheKey> factoryCacheLocks=new LockSupportMap<>(); 304 private static Cache<SimpleFactoryLookupKey, SimpleFactoryCacheKey> factoryLookupMap = null; 305 SimpleFactory()306 private SimpleFactory() { 307 } 308 getMinimalDraftStatus()309 public DraftStatus getMinimalDraftStatus() { 310 return minimalDraftStatus; 311 } 312 313 /** 314 * Create a factory from a source directory, matchingString 315 * For the matchString meaning, see {@link getMatchingXMLFiles} 316 */ make(String sourceDirectory, String matchString)317 public static Factory make(String sourceDirectory, String matchString) { 318 return make(sourceDirectory, matchString, DraftStatus.unconfirmed); 319 } 320 make(String sourceDirectory, String matchString, DraftStatus minimalDraftStatus)321 public static Factory make(String sourceDirectory, String matchString, DraftStatus minimalDraftStatus) { 322 File list[] = { new File(sourceDirectory) }; 323 if (!CACHE_SIMPLE_FACTORIES) { 324 return new SimpleFactory(list, matchString, minimalDraftStatus); 325 } 326 // we cache simple factories 327 final String sourceDirPathName = list[0].getAbsolutePath(); 328 List<String> strList = Arrays.asList(new String[] { sourceDirPathName }); 329 final SimpleFactoryCacheKey key = new SimpleFactoryCacheKey(strList, matchString, minimalDraftStatus); 330 331 synchronized (FACTORY_LOOKUP_SYNC) { 332 if (factoryCache == null) { 333 factoryCache = CacheBuilder.newBuilder().maximumSize(FACTORY_CACHE_LIMIT).build(); 334 } 335 SimpleFactory fact = factoryCache.getIfPresent(key); 336 if (fact == null) { 337 // try looking it up 338 SimpleFactoryLookupKey lookupKey = new SimpleFactoryLookupKey(sourceDirPathName, matchString); 339 if (factoryLookupMap == null) { 340 factoryLookupMap = CacheBuilder.newBuilder().maximumSize(FACTORY_CACHE_LIMIT).build(); 341 } 342 SimpleFactoryCacheKey key2 = factoryLookupMap.getIfPresent(lookupKey); 343 if (key2 != null) { 344 return factoryCache.asMap().get(key2); 345 } 346 // out of luck 347 SimpleFactory sf = new SimpleFactory(list, matchString, minimalDraftStatus); 348 factoryCache.put(key, sf); 349 if (DEBUG_SIMPLEFACTORY) { 350 System.out.println("Created new Factory with parameters " + key); 351 } 352 factoryLookupMap.put(lookupKey, key); 353 } 354 return factoryCache.asMap().get(key); 355 } 356 } 357 358 /** 359 * Create a factory from a source directory list, matchingString. 360 * For the matchString meaning, see {@link getMatchingXMLFiles} 361 */ make(File sourceDirectory[], String matchString)362 public static Factory make(File sourceDirectory[], String matchString) { 363 return make(sourceDirectory, matchString, DraftStatus.unconfirmed); 364 } 365 366 /** 367 * Create a factory from a source directory list 368 * 369 * @param sourceDirectory 370 * @param matchString 371 * @param minimalDraftStatus 372 * @return 373 */ make(File sourceDirectory[], String matchString, DraftStatus minimalDraftStatus)374 public static Factory make(File sourceDirectory[], String matchString, DraftStatus minimalDraftStatus) { 375 if (!CACHE_SIMPLE_FACTORIES) { 376 return new SimpleFactory(sourceDirectory, matchString, minimalDraftStatus); 377 } 378 379 // we cache simple factories 380 List<String> strList = new ArrayList<>(); 381 List<SimpleFactoryLookupKey> lookupList = new ArrayList<>(); 382 for (int i = 0; i < sourceDirectory.length; i++) { 383 String cur = sourceDirectory[i].getAbsolutePath(); 384 strList.add(cur); 385 lookupList.add(new SimpleFactoryLookupKey(cur, matchString)); 386 } 387 final SimpleFactoryCacheKey key = new SimpleFactoryCacheKey(strList, matchString, minimalDraftStatus); 388 synchronized (FACTORY_LOOKUP_SYNC) { 389 if (factoryCache == null) { 390 factoryCache = CacheBuilder.newBuilder().maximumSize(FACTORY_CACHE_LIMIT).build(); 391 } 392 SimpleFactory fact = factoryCache.getIfPresent(key); 393 if (fact == null) { 394 if (factoryLookupMap == null) { 395 factoryLookupMap = CacheBuilder.newBuilder().maximumSize(FACTORY_CACHE_LIMIT).build(); 396 } 397 Iterator<SimpleFactoryLookupKey> iter = lookupList.iterator(); 398 while (iter.hasNext()) { 399 SimpleFactoryLookupKey curKey = iter.next(); 400 SimpleFactoryCacheKey key2 = factoryLookupMap.asMap().get(curKey); 401 if ((key2 != null) && factoryCache.asMap().containsKey(key2)) { 402 if (DEBUG_SIMPLEFACTORY) { 403 System.out.println("Using key " + key2 + " instead of " + key + " for factory lookup"); 404 } 405 return factoryCache.asMap().get(key2); 406 } 407 } 408 SimpleFactory sf = new SimpleFactory(sourceDirectory, matchString, minimalDraftStatus); 409 if (DEBUG_SIMPLEFACTORY) { 410 System.out.println("Created new Factory with parameters " + key); 411 } 412 factoryCache.put(key, sf); 413 iter = lookupList.iterator(); 414 while (iter.hasNext()) { 415 factoryLookupMap.put(iter.next(), key); 416 } 417 } 418 return factoryCache.asMap().get(key); 419 } 420 } 421 422 @SuppressWarnings("unchecked") SimpleFactory(File sourceDirectories[], String matchString, DraftStatus minimalDraftStatus)423 private SimpleFactory(File sourceDirectories[], String matchString, DraftStatus minimalDraftStatus) { 424 // initialize class based 425 if (USE_OLD_HANDLEMAKE_CODE) { 426 mainCache = new Map[DraftStatus.values().length]; 427 resolvedCache = new Map[DraftStatus.values().length]; 428 for (int i = 0; i < mainCache.length; ++i) { 429 mainCache[i] = Collections.synchronizedMap(new LruMap<String, CLDRFile>(CACHE_LIMIT)); 430 resolvedCache[i] = Collections.synchronizedMap(new LruMap<String, CLDRFile>(CACHE_LIMIT)); 431 } 432 } else { 433 // combinedCache= Collections.synchronizedMap(new LruMap<CLDRCacheKey, CLDRFile>(CACHE_LIMIT)); 434 combinedCache = CacheBuilder.newBuilder().maximumSize(CACHE_LIMIT).build(); 435 } 436 // 437 this.sourceDirectories = sourceDirectories; 438 this.minimalDraftStatus = minimalDraftStatus; 439 Matcher m = PatternCache.get(matchString).matcher(""); 440 this.localeList = CLDRFile.getMatchingXMLFiles(sourceDirectories, m); 441 File goodSuppDir = null; 442 for (File sourceDirectoryPossibility : sourceDirectories) { 443 File suppDir = new File(sourceDirectoryPossibility, "../supplemental"); 444 if (suppDir.isDirectory()) { 445 goodSuppDir = suppDir; 446 break; 447 } 448 } 449 if (goodSuppDir != null) { 450 setSupplementalDirectory(goodSuppDir); 451 } 452 } 453 454 @Override toString()455 public String toString() { 456 StringBuilder sb = new StringBuilder("{" + getClass().getName()) 457 .append(" dirs="); 458 for (File f : sourceDirectories) { 459 sb.append(f.getPath()).append(' '); 460 } 461 sb.append('}'); 462 return sb.toString(); 463 } 464 handleGetAvailable()465 protected Set<String> handleGetAvailable() { 466 return localeList; 467 } 468 469 public static class NoSourceDirectoryException extends ICUUncheckedIOException { 470 private static final long serialVersionUID = 1L; 471 private final String localeName; 472 NoSourceDirectoryException(String localeName)473 public NoSourceDirectoryException(String localeName) { 474 this.localeName = localeName; 475 } 476 477 @Override getMessage()478 public String getMessage() { 479 return "Unable to determine the source directory for locale " + localeName; 480 } 481 } 482 483 /** 484 * Make a CLDR file. The result is a locked file, so that it can be cached. If you want to modify it, 485 * use clone(). 486 */ 487 @SuppressWarnings("unchecked") handleMake(String localeName, boolean resolved, DraftStatus minimalDraftStatus)488 public CLDRFile handleMake(String localeName, boolean resolved, DraftStatus minimalDraftStatus) { 489 @SuppressWarnings("rawtypes") 490 final Map mapToSynchronizeOn; 491 final List<File> parentDirs = getSourceDirectoriesForLocale(localeName); 492 /* 493 * Parameter check: parentDir being null means the source directory could not be found - throw exception here 494 * rather than running into a NullPointerException when trying to create/store the cache key further down. 495 */ 496 if (parentDirs == null) { 497 // changed from IllegalArgumentException, which does't let us filter exceptions. 498 throw new NoSourceDirectoryException(localeName); 499 } 500 final Object cacheKey; 501 CLDRFile result; // result of the lookup / generation 502 if (USE_OLD_HANDLEMAKE_CODE) { 503 final Map<String, CLDRFile> cache = resolved ? resolvedCache[minimalDraftStatus.ordinal()] : mainCache[minimalDraftStatus.ordinal()]; 504 mapToSynchronizeOn = cache; 505 cacheKey = localeName; 506 result = cache.get(localeName); 507 } else { 508 // Use double-check idiom 509 cacheKey = new CLDRCacheKey(localeName, resolved, minimalDraftStatus, parentDirs); 510 // result = cache.get(localeName); 511 // result=combinedCache.asMap().get(cacheKey); 512 result = combinedCache.getIfPresent(cacheKey); 513 mapToSynchronizeOn = combinedCache.asMap(); 514 } 515 if (result != null) { 516 if (DEBUG_SIMPLEFACTORY) { 517 System.out.println("HandleMake:Returning cached result for locale " + localeName); 518 } 519 return result; 520 } 521 // synchronized (cache) { 522 synchronized (mapToSynchronizeOn) { 523 // Check cache twice to ensure that CLDRFile is only loaded once 524 // even with multiple threads. 525 // result = cache.get(localeName); 526 // result=combinedCache.get(cacheKey); 527 Object returned = mapToSynchronizeOn.get(cacheKey); 528 if (returned instanceof CLDRFile) { 529 result = (CLDRFile) returned; 530 } 531 if (result != null) { 532 if (DEBUG_SIMPLEFACTORY) { 533 System.out.println("HandleMake:Returning cached result for locale " + localeName); 534 } 535 return result; 536 } 537 if (resolved) { 538 ResolvingSource makeResolvingSource; 539 try { 540 makeResolvingSource = makeResolvingSource(localeName, minimalDraftStatus); 541 } catch (Exception e) { 542 throw new ICUException("Couldn't make resolved CLDR file for " + localeName, e); 543 } 544 result = new CLDRFile(makeResolvingSource); 545 } else { 546 if (parentDirs != null) { 547 if (DEBUG_SIMPLEFACTORY) { 548 StringBuilder sb = new StringBuilder(); 549 sb.append("HandleMake: Calling makeFile with locale: "); 550 sb.append(localeName); 551 sb.append(", parentDir: "); 552 sb.append(parentDirs); 553 sb.append(", DraftStatus: "); 554 sb.append(minimalDraftStatus); 555 System.out.println(sb.toString()); 556 } 557 result = makeFile(localeName, parentDirs, minimalDraftStatus); 558 result.freeze(); 559 } 560 } 561 if (result != null) { 562 mapToSynchronizeOn.put(cacheKey, result); 563 // combinedCache.put(cacheKey, result); 564 // cache.put(localeName, result); 565 } 566 return result; 567 } 568 } 569 570 /** 571 * Produce a CLDRFile from a localeName, given a directory. 572 * 573 * @param localeName 574 * @param dir 575 * directory 576 */ 577 // TODO make the directory a URL makeFromFile(String fullFileName, String localeName, DraftStatus minimalDraftStatus)578 public static CLDRFile makeFromFile(String fullFileName, String localeName, DraftStatus minimalDraftStatus) { 579 return makeFromFile(new File(fullFileName), localeName, minimalDraftStatus); 580 } 581 makeFromFile(File file, String localeName, DraftStatus minimalDraftStatus)582 private static CLDRFile makeFromFile(File file, String localeName, DraftStatus minimalDraftStatus) { 583 return CLDRFile.loadFromFile(file, localeName, minimalDraftStatus); 584 } 585 makeFromFile(List<File> dirs, String localeName, DraftStatus minimalDraftStatus)586 private static CLDRFile makeFromFile(List<File> dirs, String localeName, DraftStatus minimalDraftStatus) { 587 return CLDRFile.loadFromFiles(dirs, localeName, minimalDraftStatus); 588 } 589 590 /** 591 * Create a CLDRFile for the given localename. 592 * 593 * @param localeName 594 */ makeSupplemental(String localeName)595 public static CLDRFile makeSupplemental(String localeName) { 596 XMLSource source = new SimpleXMLSource(localeName); 597 CLDRFile result = new CLDRFile(source); 598 result.setNonInheriting(true); 599 return result; 600 } 601 602 /** 603 * CLDRFile from a file input stream. Set the locale ID from the same input stream. 604 * 605 * @param fileName 606 * @param fis 607 * @param minimalDraftStatus 608 * @return 609 */ makeFile(String fileName, InputStream fis, CLDRFile.DraftStatus minimalDraftStatus)610 public static CLDRFile makeFile(String fileName, InputStream fis, CLDRFile.DraftStatus minimalDraftStatus) { 611 CLDRFile file = CLDRFile.load(fileName, null, fis, minimalDraftStatus); 612 return file; 613 } 614 615 /** 616 * Produce a CLDRFile from a file input stream. 617 * 618 * @param localeName 619 * @param fis 620 */ makeFile(String fileName, String localeName, InputStream fis, CLDRFile.DraftStatus minimalDraftStatus)621 public static CLDRFile makeFile(String fileName, String localeName, InputStream fis, 622 CLDRFile.DraftStatus minimalDraftStatus) { 623 return CLDRFile.load(fileName, localeName, fis, minimalDraftStatus); 624 } 625 makeFile(String localeName, String dir, CLDRFile.DraftStatus minimalDraftStatus)626 public static CLDRFile makeFile(String localeName, String dir, CLDRFile.DraftStatus minimalDraftStatus) { 627 return makeFile(localeName, new File(dir), minimalDraftStatus); 628 } 629 makeFile(String localeName, File dir, CLDRFile.DraftStatus minimalDraftStatus)630 public static CLDRFile makeFile(String localeName, File dir, CLDRFile.DraftStatus minimalDraftStatus) { 631 CLDRFile file = makeFromFile(makeFileName(localeName, dir), localeName, minimalDraftStatus); 632 return file; 633 } 634 makeFile(String localeName, List<File> dirs, CLDRFile.DraftStatus minimalDraftStatus)635 public static CLDRFile makeFile(String localeName, List<File> dirs, CLDRFile.DraftStatus minimalDraftStatus) { 636 CLDRFile file = makeFromFile(dirs, localeName, minimalDraftStatus); 637 return file; 638 } 639 640 /** 641 * @param localeName 642 * @param dir 643 * @return 644 */ makeFileName(String localeName, File dir)645 private static File makeFileName(String localeName, File dir) { 646 return new File(dir, localeName + ".xml"); 647 } 648 649 /** 650 * Create a CLDRFile for the given localename. 651 * SimpleXMLSource will be used as the source. 652 * 653 * @param localeName 654 */ makeFile(String localeName)655 public static CLDRFile makeFile(String localeName) { 656 XMLSource source = new SimpleXMLSource(localeName); 657 return new CLDRFile(source); 658 } 659 660 /** 661 * Produce a CLDRFile from a localeName and filename, given a directory. 662 * 663 * @param localeName 664 * @param dir 665 * directory 666 */ makeFile(String localeName, String dir, boolean includeDraft)667 public static CLDRFile makeFile(String localeName, String dir, boolean includeDraft) { 668 return makeFile(localeName, dir, includeDraft ? CLDRFile.DraftStatus.unconfirmed 669 : CLDRFile.DraftStatus.approved); 670 } 671 672 @Override getSourceDirectories()673 public File[] getSourceDirectories() { 674 return sourceDirectories; 675 } 676 677 @Override getSourceDirectoriesForLocale(String localeName)678 public List<File> getSourceDirectoriesForLocale(String localeName) { 679 Builder<File> result = null; 680 boolean isSupplemental = CLDRFile.isSupplementalName(localeName); 681 for (File sourceDirectory : this.sourceDirectories) { 682 if (isSupplemental) { 683 sourceDirectory = new File( 684 sourceDirectory.getAbsolutePath().replace("incoming" + File.separator + "vetted" + File.separator, "common" + File.separator)); 685 } 686 final File dir = isSupplemental ? new File(sourceDirectory, "../supplemental") : sourceDirectory; 687 final File xmlFile = makeFileName(localeName, dir); 688 if (xmlFile.canRead()) { 689 if (result == null) { 690 result = ImmutableList.<File> builder(); 691 } 692 result.add(dir); 693 } 694 } 695 return result == null ? null : result.build(); 696 } 697 698 }