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