• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }