1 // © 2019 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 package org.unicode.icu.tool.cldrtoicu; 4 5 import static com.google.common.base.Preconditions.checkNotNull; 6 import static java.util.stream.Collectors.joining; 7 8 import java.io.PrintWriter; 9 import java.util.List; 10 import java.util.Map; 11 import java.util.TreeMap; 12 13 /** 14 * Stores any explicit locale relationships for a single directory (e.g. "lang" or "coll"). 15 * This class just reflects a concise version of the "%%Parent and %%ALIAS" paths set in files and 16 * allows them to be written to the dependency graph files in each ICU data directory. 17 */ 18 final class DependencyGraph { 19 private final String cldrVersion; 20 private final Map<String, String> parentMap = new TreeMap<>(); 21 private final Map<String, String> aliasMap = new TreeMap<>(); 22 DependencyGraph(String cldrVersion)23 public DependencyGraph(String cldrVersion) { 24 this.cldrVersion = checkNotNull(cldrVersion); 25 } 26 addParent(String localeId, String parentId)27 void addParent(String localeId, String parentId) { 28 // Aliases take priority (since they can be forced and will replace empty files). Note 29 // however that this only happens in a tiny number of places due to the somewhat "hacky" 30 // forced aliases, and in future it's perfectly possibly that there would never be an 31 // overlap, and this code could just prohibit overlap between alias and parent mappings. 32 if (!aliasMap.containsKey(localeId)) { 33 parentMap.put(localeId, parentId); 34 } 35 } 36 addAlias(String sourceId, String targetId)37 void addAlias(String sourceId, String targetId) { 38 parentMap.remove(sourceId); 39 aliasMap.put(sourceId, targetId); 40 } 41 42 /** 43 * Outputs a JSON dictionary containing the parent and alias mappings to the given writer. The 44 * output contains non-JSON line comments and is of the form: 45 * <pre>{@code 46 * // <copyright message> 47 * { 48 * "cldrVersion": "<version>" 49 * "aliases": { 50 * "<source>": "<target>" 51 * ... 52 * } 53 * "parents": { 54 * "<id>": "<parent>" 55 * ... 56 * } 57 * } 58 * }</pre> 59 * where all values (other than the version) are locale IDs. 60 * 61 * <p>Anything reading the produced files must strip the line comments prior to processing the 62 * JSON data. Line comments only appear as a contiguous block in the header, so comment 63 * processing can stop at the first non-comment line (i.e. the first bare '{'). 64 */ writeJsonTo(PrintWriter out, List<String> fileHeader)65 void writeJsonTo(PrintWriter out, List<String> fileHeader) { 66 fileHeader.forEach(s -> out.println("// " + s)); 67 out.println(); 68 out.format("{\n \"cldrVersion\": \"%s\"", cldrVersion); 69 writeMap(out, "aliases", aliasMap); 70 writeMap(out, "parents", parentMap); 71 out.append("\n}\n"); 72 out.close(); 73 } 74 writeMap(PrintWriter out, String name, Map<String, String> map)75 private static void writeMap(PrintWriter out, String name, Map<String, String> map) { 76 if (!map.isEmpty()) { 77 out.append( 78 map.entrySet().stream() 79 .map(e -> String.format("\n \"%s\": \"%s\"", e.getKey(), e.getValue())) 80 .collect(joining(",", ",\n \"" + name + "\": {", "\n }"))); 81 } 82 } 83 } 84