blob: b35dc300502b3fa8c633d47c17f94020fa28f0cf [file] [log] [blame]
package org.unicode.cldr.tool;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.unicode.cldr.draft.FileUtilities;
import org.unicode.cldr.test.ExampleGenerator;
import org.unicode.cldr.util.CLDRConfig;
import org.unicode.cldr.util.CLDRFile;
import org.unicode.cldr.util.CLDRPaths;
import org.unicode.cldr.util.CLDRTool;
import org.unicode.cldr.util.Factory;
import org.unicode.cldr.util.LocaleIDParser;
import org.unicode.cldr.util.PathStarrer;
import org.unicode.cldr.util.RecordingCLDRFile;
import org.unicode.cldr.util.XMLSource;
@CLDRTool(alias = "generate-example-dependencies", description = "Generate example dependencies")
public class GenerateExampleDependencies {
private static final String OUTPUT_FILE_NAME = "ExampleDependencies.java";
private static final String DO_NOT_EDIT =
"/* DO NOT EDIT THIS FILE, instead regenerate it using "
+ GenerateExampleDependencies.class.getSimpleName()
+ ".java */";
private final CLDRFile englishFile;
private final Factory factory;
private final Set<String> locales;
private final String outputDir;
private final PathStarrer pathStarrer;
public static void main(String[] args) throws IOException {
new GenerateExampleDependencies().run();
}
/**
* Find dependencies where changing the value of one path changes example-generation for another
* path.
*
* <p>The goal is to optimize example caching by only regenerating examples when necessary.
*/
public GenerateExampleDependencies() {
CLDRConfig info = CLDRConfig.getInstance();
englishFile = info.getEnglish();
factory = info.getCldrFactory();
locales = factory.getAvailable();
outputDir = CLDRPaths.GEN_DIRECTORY + "test" + File.separator;
pathStarrer = new PathStarrer().setSubstitutionPattern("*");
}
public void run() throws IOException {
int localeCount = locales.size();
System.out.println(
"Looping through " + localeCount + " locales ... (this may take an hour)");
final Multimap<String, String> dependencies = TreeMultimap.create();
int i = 0;
for (String localeId : locales) {
int percent = (++i * 100) / localeCount;
System.out.println(localeId + " " + i + "/" + localeCount + " " + percent + "%");
addDependenciesForLocale(dependencies, localeId);
}
System.out.println("Creating " + outputDir + OUTPUT_FILE_NAME + " ...");
PrintWriter writer = FileUtilities.openUTF8Writer(outputDir, OUTPUT_FILE_NAME);
int dependenciesWritten = writeDependenciesToFile(dependencies, writer);
System.out.println(
"Wrote "
+ dependenciesWritten
+ " dependencies to "
+ outputDir
+ GenerateExampleDependencies.OUTPUT_FILE_NAME);
System.out.println(
"If it looks OK, you can move it to the proper location, replacing the old version.");
}
private void addDependenciesForLocale(Multimap<String, String> dependencies, String localeId) {
RecordingCLDRFile cldrFile = makeRecordingCldrFile(localeId);
cldrFile.disableCaching();
Set<String> paths = new TreeSet<>(cldrFile.getComparator());
cldrFile.forEach(paths::add); // time-consuming
ExampleGenerator egTest = new ExampleGenerator(cldrFile, englishFile);
// Caching MUST be disabled for egTest.ICUServiceBuilder to prevent some dependencies from
// being missed
egTest.setCachingEnabled(false);
for (String pathB : paths) {
if (skipPathForDependencies(pathB)) {
continue;
}
String valueB = cldrFile.getStringValue(pathB);
if (valueB == null) {
continue;
}
String starredB = pathStarrer.set(pathB);
cldrFile.clearRecordedPaths();
egTest.getExampleHtml(pathB, valueB);
HashSet<String> pathsA = cldrFile.getRecordedPaths();
for (String pathA : pathsA) {
if (pathA.equals(pathB) || skipPathForDependencies(pathA)) {
continue;
}
String starredA = pathStarrer.set(pathA);
dependencies.put(starredA, starredB);
}
}
}
private RecordingCLDRFile makeRecordingCldrFile(String localeId) {
XMLSource topSource = factory.makeSource(localeId);
List<XMLSource> parents = getParentSources(factory, localeId);
XMLSource[] a = new XMLSource[parents.size()];
return new RecordingCLDRFile(topSource, parents.toArray(a));
}
/**
* Get the parent sources for the given localeId
*
* @param factory the Factory for makeSource
* @param localeId the locale ID
* @return the List of XMLSource objects
*/
private static List<XMLSource> getParentSources(Factory factory, String localeId) {
List<XMLSource> parents = new ArrayList<>();
for (String currentLocaleID = LocaleIDParser.getParent(localeId);
currentLocaleID != null;
currentLocaleID = LocaleIDParser.getParent(currentLocaleID)) {
parents.add(factory.makeSource(currentLocaleID));
}
return parents;
}
/**
* Should the given path be skipped when testing example-generator path dependencies?
*
* @param path the path in question
* @return true to skip, else false
*/
private static boolean skipPathForDependencies(String path) {
return path.endsWith("/alias") || path.startsWith("//ldml/identity");
}
/**
* Write the given map of example-generator path dependencies to a java file.
*
* @param dependencies the multimap of example-generator path dependencies
* @param writer the PrintWriter
* @return the number of dependencies written
*/
private int writeDependenciesToFile(Multimap<String, String> dependencies, PrintWriter writer) {
writer.println("package org.unicode.cldr.test;");
writer.println(DO_NOT_EDIT);
writer.println("import com.google.common.collect.ImmutableSetMultimap;");
writer.println("");
writer.println("public class ExampleDependencies {");
writer.println(" public static ImmutableSetMultimap<String, String> dependencies =");
writer.println(" new ImmutableSetMultimap.Builder<String, String>()");
int dependenciesWritten = 0;
ArrayList<String> listA = new ArrayList<>(dependencies.keySet());
Collections.sort(listA);
for (String pathA : listA) {
ArrayList<String> listB = new ArrayList<>(dependencies.get(pathA));
Collections.sort(listB);
String a = "\"" + pathA.replaceAll("\"", "\\\\\"") + "\"";
writer.println(" .putAll(" + a + ",");
int remainingCount = listB.size();
for (String pathB : listB) {
String b = "\"" + pathB.replaceAll("\"", "\\\\\"") + "\"";
String endOfLine = --remainingCount > 0 ? "," : ")";
writer.println(" " + b + endOfLine);
++dependenciesWritten;
}
}
writer.println(" .build();");
writer.println("}");
writer.println(DO_NOT_EDIT);
writer.close();
return dependenciesWritten;
}
}