1 package org.unicode.cldr.util; 2 3 import java.io.File; 4 import java.util.ArrayList; 5 import java.util.Collections; 6 import java.util.Iterator; 7 import java.util.List; 8 import java.util.Set; 9 import java.util.TreeSet; 10 11 import org.unicode.cldr.util.CLDRFile.DraftStatus; 12 import org.unicode.cldr.util.CLDRLocale.SublocaleProvider; 13 import org.unicode.cldr.util.XMLSource.ResolvingSource; 14 15 /** 16 * A factory is the normal method to produce a set of CLDRFiles from a directory of XML files. 17 * See SimpleFactory for a concrete subclass. 18 */ 19 public abstract class Factory implements SublocaleProvider { 20 private boolean ignoreExplicitParentLocale = false; 21 22 /** 23 * Whether to ignore explicit parent locale / fallback script behavior 24 * with a resolving source. 25 * 26 * Long story short, call setIgnoreExplictParentLocale(true) for collation trees. 27 */ setIgnoreExplicitParentLocale(boolean newIgnore)28 public Factory setIgnoreExplicitParentLocale(boolean newIgnore) { 29 ignoreExplicitParentLocale = newIgnore; 30 return this; 31 } 32 33 /** 34 * Flag to set more verbose output in makeServolingSource 35 */ 36 private static final boolean DEBUG_FACTORY = false; 37 38 private File supplementalDirectory = null; 39 40 /** 41 * Note, the source director(ies) may be a list (seed/common). Therefore, this function is deprecated 42 * 43 * @deprecated 44 * @return the first directory 45 */ 46 @Deprecated getSourceDirectory()47 public String getSourceDirectory() { 48 return getSourceDirectories()[0].getAbsolutePath(); 49 } 50 51 /** 52 * Note, the source director(ies) may be a list (seed/common). 53 * 54 * @return the first directory 55 */ getSourceDirectories()56 public abstract File[] getSourceDirectories(); 57 58 /** 59 * Which source directory does this particular localeID belong to? 60 * 61 * @param localeID 62 * @return 63 */ 64 @Deprecated getSourceDirectoryForLocale(String localeID)65 public final File getSourceDirectoryForLocale(String localeID) { 66 List<File> temp = getSourceDirectoriesForLocale(localeID); 67 return temp == null ? null : temp.get(0); 68 } 69 70 /** 71 * Classify the tree according to type (maturity) 72 * 73 * @author srl 74 * 75 */ 76 public enum SourceTreeType { 77 common, seed, other 78 } 79 80 /** 81 * Returns the source tree type of either an XML file or its parent directory. 82 * 83 * @param fileOrDir 84 * @return 85 */ getSourceTreeType(File fileOrDir)86 public static final SourceTreeType getSourceTreeType(File fileOrDir) { 87 if (fileOrDir == null) return null; 88 File parentDir = fileOrDir.isFile() ? fileOrDir.getParentFile() : fileOrDir; 89 File grandparentDir = parentDir.getParentFile(); 90 91 try { 92 return SourceTreeType.valueOf(grandparentDir.getName()); 93 } catch (IllegalArgumentException iae) { 94 try { 95 return SourceTreeType.valueOf(parentDir.getName()); 96 } catch (IllegalArgumentException iae2) { 97 return SourceTreeType.other; 98 } 99 } 100 } 101 102 public enum DirectoryType { 103 main, supplemental, bcp47, casing, collation, dtd, rbnf, segments, transforms, other 104 } 105 getDirectoryType(File fileOrDir)106 public static final DirectoryType getDirectoryType(File fileOrDir) { 107 if (fileOrDir == null) return null; 108 File parentDir = fileOrDir.isFile() ? fileOrDir.getParentFile() : fileOrDir; 109 110 try { 111 return DirectoryType.valueOf(parentDir.getName()); 112 } catch (IllegalArgumentException iae2) { 113 return DirectoryType.other; 114 } 115 } 116 handleMake(String localeID, boolean resolved, DraftStatus madeWithMinimalDraftStatus)117 protected abstract CLDRFile handleMake(String localeID, boolean resolved, DraftStatus madeWithMinimalDraftStatus); 118 make(String localeID, boolean resolved, DraftStatus madeWithMinimalDraftStatus)119 public CLDRFile make(String localeID, boolean resolved, DraftStatus madeWithMinimalDraftStatus) { 120 return handleMake(localeID, resolved, madeWithMinimalDraftStatus) 121 .setSupplementalDirectory(getSupplementalDirectory()); 122 } 123 make(String localeID, boolean resolved, boolean includeDraft)124 public CLDRFile make(String localeID, boolean resolved, boolean includeDraft) { 125 return make(localeID, resolved, includeDraft ? DraftStatus.unconfirmed : DraftStatus.approved); 126 } 127 make(String localeID, boolean resolved)128 public CLDRFile make(String localeID, boolean resolved) { 129 return make(localeID, resolved, getMinimalDraftStatus()); 130 } 131 makeWithFallback(String localeID)132 public CLDRFile makeWithFallback(String localeID) { 133 return makeWithFallback(localeID, getMinimalDraftStatus()); 134 } 135 makeWithFallback(String localeID, DraftStatus madeWithMinimalDraftStatus)136 public CLDRFile makeWithFallback(String localeID, DraftStatus madeWithMinimalDraftStatus) { 137 String currentLocaleID = localeID; 138 Set<String> availableLocales = this.getAvailable(); 139 while (!availableLocales.contains(currentLocaleID) && !"root".equals(currentLocaleID)) { 140 currentLocaleID = LocaleIDParser.getParent(currentLocaleID, ignoreExplicitParentLocale); 141 } 142 return make(currentLocaleID, true, madeWithMinimalDraftStatus); 143 } 144 makeResolvingSource(List<XMLSource> sources)145 public static XMLSource makeResolvingSource(List<XMLSource> sources) { 146 return new ResolvingSource(sources); 147 } 148 149 /** 150 * Temporary wrapper for creating an XMLSource. This is a hack and should 151 * only be used in the Survey Tool for now. 152 * 153 * @param localeID 154 * @return 155 */ makeSource(String localeID)156 public final XMLSource makeSource(String localeID) { 157 return make(localeID, false).dataSource; 158 } 159 160 /** 161 * Creates a resolving source for the given locale ID. 162 * 163 * @param localeID 164 * @param madeWithMinimalDraftStatus 165 * @return 166 */ makeResolvingSource(String localeID, DraftStatus madeWithMinimalDraftStatus)167 protected ResolvingSource makeResolvingSource(String localeID, DraftStatus madeWithMinimalDraftStatus) { 168 List<XMLSource> sourceList = new ArrayList<>(); 169 String curLocale = localeID; 170 while (curLocale != null) { 171 if (DEBUG_FACTORY) { 172 System.out.println("Factory.makeResolvingSource: calling handleMake for locale " + 173 curLocale + " and MimimalDraftStatus " + madeWithMinimalDraftStatus); 174 } 175 CLDRFile file = handleMake(curLocale, false, madeWithMinimalDraftStatus); 176 if (file == null) { 177 throw new NullPointerException(this + ".handleMake returned a null CLDRFile for " + curLocale); 178 } 179 XMLSource source = file.dataSource; 180 sourceList.add(source); 181 curLocale = LocaleIDParser.getParent(curLocale, ignoreExplicitParentLocale); 182 } 183 return new ResolvingSource(sourceList); 184 } 185 getMinimalDraftStatus()186 public abstract DraftStatus getMinimalDraftStatus(); 187 188 /** 189 * Convenience static 190 * 191 * @param path 192 * @param string 193 * @return 194 */ make(String path, String string)195 public static Factory make(String path, String string) { 196 try { 197 return SimpleFactory.make(path, string); 198 } catch (Exception e) { 199 throw new IllegalArgumentException("path: " + path + "; string: " + string, e); 200 } 201 } 202 203 /** 204 * Convenience static 205 * 206 * @param mainDirectory 207 * @param string 208 * @param approved 209 * @return 210 */ make(String mainDirectory, String string, DraftStatus approved)211 public static Factory make(String mainDirectory, String string, DraftStatus approved) { 212 return SimpleFactory.make(mainDirectory, string, approved); 213 } 214 215 /** 216 * Get a set of the available locales for the factory. 217 */ getAvailable()218 public Set<String> getAvailable() { 219 return Collections.unmodifiableSet(handleGetAvailable()); 220 } 221 handleGetAvailable()222 protected abstract Set<String> handleGetAvailable(); 223 224 /** 225 * Get a set of the available language locales (according to isLanguage). 226 */ getAvailableLanguages()227 public Set<String> getAvailableLanguages() { 228 Set<String> result = new TreeSet<>(); 229 for (Iterator<String> it = handleGetAvailable().iterator(); it.hasNext();) { 230 String s = it.next(); 231 if (XPathParts.isLanguage(s)) result.add(s); 232 } 233 return result; 234 } 235 236 /** 237 * Get a set of the locales that have the given parent (according to isSubLocale()) 238 * 239 * @param isProper 240 * if false, then parent itself will match 241 */ getAvailableWithParent(String parent, boolean isProper)242 public Set<String> getAvailableWithParent(String parent, boolean isProper) { 243 Set<String> result = new TreeSet<>(); 244 245 for (Iterator<String> it = handleGetAvailable().iterator(); it.hasNext();) { 246 String s = it.next(); 247 int relation = XPathParts.isSubLocale(parent, s); 248 if (relation >= 0 && !(isProper && relation == 0)) result.add(s); 249 } 250 return result; 251 } 252 getSupplementalDirectory()253 public File getSupplementalDirectory() { 254 return supplementalDirectory; 255 } 256 257 /** 258 * Sets the supplemental directory to be used by this Factory and CLDRFiles 259 * created by this Factory. 260 * 261 * @param supplementalDirectory 262 * @return 263 */ setSupplementalDirectory(File supplementalDirectory)264 public Factory setSupplementalDirectory(File supplementalDirectory) { 265 this.supplementalDirectory = supplementalDirectory; 266 return this; 267 } 268 269 // TODO(jchye): Clean this up. getSupplementalData()270 public CLDRFile getSupplementalData() { 271 try { 272 return make("supplementalData", false); 273 } catch (RuntimeException e) { 274 return Factory.make(getSupplementalDirectory().getPath(), ".*").make("supplementalData", false); 275 } 276 } 277 getSupplementalMetadata()278 public CLDRFile getSupplementalMetadata() { 279 try { 280 return make("supplementalMetadata", false); 281 } catch (RuntimeException e) { 282 return Factory.make(getSupplementalDirectory().getPath(), ".*").make("supplementalMetadata", false); 283 } 284 } 285 286 /** 287 * These factory implementations don't do any caching. 288 */ 289 @Override subLocalesOf(CLDRLocale forLocale)290 public Set<CLDRLocale> subLocalesOf(CLDRLocale forLocale) { 291 return calculateSubLocalesOf(forLocale, getAvailableCLDRLocales()); 292 } 293 294 /** 295 * Helper function. 296 * 297 * @return 298 */ getAvailableCLDRLocales()299 public Set<CLDRLocale> getAvailableCLDRLocales() { 300 return CLDRLocale.getInstance(getAvailable()); 301 } 302 303 /** 304 * Helper function. Does not cache. 305 * 306 * @param locale 307 * @param available 308 * @return 309 */ calculateSubLocalesOf(CLDRLocale locale, Set<CLDRLocale> available)310 public Set<CLDRLocale> calculateSubLocalesOf(CLDRLocale locale, Set<CLDRLocale> available) { 311 Set<CLDRLocale> sub = new TreeSet<>(); 312 for (CLDRLocale l : available) { 313 if (l.getParent() == locale) { 314 sub.add(l); 315 } 316 } 317 return sub; 318 } 319 320 /** 321 * Get all of the files in the source directories that match localeName (which is really xml file name). 322 * @param localeName 323 * @return 324 */ getSourceDirectoriesForLocale(String localeName)325 public abstract List<File> getSourceDirectoriesForLocale(String localeName); 326 } 327