• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.icu;
2 
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Map.Entry;
13 import java.util.Set;
14 
15 import org.unicode.cldr.icu.ResourceSplitter.SplitInfo;
16 
17 public class IcuDataSplitter {
18     private static final String VERSION_PATH = "/Version";
19     private static final String PARENT_PATH = "/%%Parent";
20 
21     private final List<SplitInfo> splitInfos;
22     private final Map<String, File> targetDirs;
23     private final Map<String, Set<String>> splitSources = new HashMap<String, Set<String>>();
24 
25     /**
26      * Splits the
27      *
28      * @param splitInfos
29      */
IcuDataSplitter(List<SplitInfo> splitInfos)30     private IcuDataSplitter(List<SplitInfo> splitInfos) {
31         this.splitInfos = splitInfos;
32         targetDirs = new HashMap<String, File>();
33     }
34 
35     /**
36      * Creates a new IcuDataSplitter and creates directories for the split files
37      * if they do not already exist.
38      *
39      * @param mainDirPath
40      *            the main directory that other directories will be relative to.
41      * @param splitInfos
42      * @return
43      */
make(String mainDirPath, List<SplitInfo> splitInfos)44     public static IcuDataSplitter make(String mainDirPath, List<SplitInfo> splitInfos) {
45         IcuDataSplitter splitter = new IcuDataSplitter(splitInfos);
46         // Make sure that all the required directories are present.
47         Map<String, File> targetDirs = splitter.targetDirs;
48         for (SplitInfo si : splitInfos) {
49             String dirPath = si.targetDirPath;
50             if (!targetDirs.containsKey(dirPath)) {
51                 File dir = new File(dirPath);
52                 if (!dir.isAbsolute()) {
53                     dir = new File(mainDirPath, "/../" + dirPath);
54                 }
55                 if (dir.exists()) {
56                     if (!dir.isDirectory()) {
57                         throw new IllegalArgumentException(
58                             "File \"" + dirPath + "\" exists and is not a directory");
59                     }
60                     if (!dir.canWrite()) {
61                         throw new IllegalArgumentException(
62                             "Cannot write to directory \"" + dirPath + "\"");
63                     }
64                 } else {
65                     if (!dir.mkdirs()) {
66                         String canonicalPath;
67                         try {
68                             canonicalPath = dir.getCanonicalPath();
69                         } catch (IOException e) {
70                             canonicalPath = dirPath;
71                         }
72                         throw new IllegalArgumentException(
73                             "Unable to create directory path \"" + canonicalPath + "\"");
74                     }
75                 }
76                 targetDirs.put(dirPath, dir);
77             }
78         }
79         return splitter;
80     }
81 
82     /**
83      * Splits an IcuData object for writing to different directories.
84      *
85      * @param data
86      * @return
87      */
split(IcuData icuData, String fallbackDir)88     public Map<String, IcuData> split(IcuData icuData, String fallbackDir) {
89         Map<String, IcuData> splitData = new HashMap<String, IcuData>();
90         String sourceFile = icuData.getSourceFile();
91         String name = icuData.getName();
92         boolean hasFallback = icuData.hasFallback();
93         Set<String> dirs = targetDirs.keySet();
94         for (String dir : dirs) {
95             splitData.put(dir, new IcuData(sourceFile, name, hasFallback));
96         }
97         splitData.put(fallbackDir, new IcuData(sourceFile, name, hasFallback));
98 
99         for (Entry<String, List<String[]>> entry : icuData.entrySet()) {
100             String rbPath = entry.getKey();
101             List<String[]> values = entry.getValue();
102             boolean wasSplit = false;
103             // Paths that should be copied to all directories.
104             if (rbPath.equals(VERSION_PATH) || rbPath.equals(PARENT_PATH)) {
105                 for (String dir : dirs) {
106                     splitData.get(dir).addAll(rbPath, values);
107                 }
108             } else {
109                 // Split up regular paths.
110                 for (SplitInfo splitInfo : splitInfos) {
111                     String checkPath = rbPath.replaceFirst(":alias", "/"); // Handle splitting of a top level alias ( as in root/units )
112                     if (checkPath.startsWith(splitInfo.srcNodePath)) {
113                         splitData.get(splitInfo.targetDirPath).addAll(rbPath, values);
114                         wasSplit = true;
115                         break;
116                     }
117                 }
118             }
119             // Add any remaining values to the file in fallback dir.
120             if (!wasSplit) {
121                 splitData.get(fallbackDir).addAll(rbPath, values);
122             }
123         }
124         // Remove all files that only contain version info.
125         Iterator<Entry<String, IcuData>> iterator = splitData.entrySet().iterator();
126         String comment = icuData.getFileComment();
127         while (iterator.hasNext()) {
128             Entry<String, IcuData> entry = iterator.next();
129             IcuData data = entry.getValue();
130             data.setFileComment(comment);
131             if (entry.getKey().equals(fallbackDir)) continue;
132             if (data.size() == 1 && data.containsKey(VERSION_PATH)) {
133                 // Comment copied from ResourceSplitter:
134                 // Some locales that use root data rely on the presence of
135                 // a resource file matching the prefix of the locale to prevent fallback
136                 // lookup through the default locale. To prevent this error, all resources
137                 // need at least a language-only stub resource to be present.
138                 //
139                 // Arrgh. The icu package tool wants all internal nodes in the tree to be
140                 // present. Currently, the missing nodes are all lang_Script locales.
141                 // Maybe change the package tool to fix this.
142                 String locale = data.getName();
143                 int underscorePos = locale.indexOf('_');
144                 if (underscorePos > -1 && locale.length() - underscorePos - 1 != 4) {
145                     iterator.remove();
146                     continue;
147                 }
148             }
149             add(splitSources, entry.getKey(), data.getName());
150         }
151         return splitData;
152     }
153 
154     /**
155      * Adds a value to the list with the specified key.
156      */
add(Map<String, Set<String>> map, String key, String value)157     private static void add(Map<String, Set<String>> map, String key, String value) {
158         Set<String> set = map.get(key);
159         if (set == null) {
160             map.put(key, set = new HashSet<String>());
161         }
162         set.add(value);
163     }
164 
165     /**
166      * Returns the set of directories that the splitter splits data into (excluding the main directory).
167      */
getTargetDirs()168     public Set<String> getTargetDirs() {
169         return targetDirs.keySet();
170     }
171 
getDirSources(String dir)172     public Set<String> getDirSources(String dir) {
173         return Collections.unmodifiableSet(splitSources.get(dir));
174     }
175 
generateMakefile(Collection<String> aliases, String dir)176     public Makefile generateMakefile(Collection<String> aliases, String dir) {
177         String prefix = dir.toUpperCase();
178         Makefile makefile = new Makefile(prefix);
179         makefile.addSyntheticAlias(aliases);
180         makefile.addAliasSource();
181         makefile.addSource(splitSources.get(dir));
182         return makefile;
183     }
184 }
185