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