• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.util;
2 
3 import com.google.common.collect.ImmutableMap;
4 import com.google.common.collect.ImmutableSet;
5 import com.ibm.icu.text.Transform;
6 import java.util.Collections;
7 import java.util.LinkedHashMap;
8 import java.util.Map;
9 import org.unicode.cldr.test.CheckCLDR.CheckStatus.Subtype;
10 import org.unicode.cldr.util.RegexLookup.Merger;
11 
12 public class PatternPlaceholders {
13 
14     public enum PlaceholderStatus {
15         DISALLOWED("No placeholders allowed."), //
16         REQUIRED("Specific number of placeholders allowed."), //
17         LOCALE_DEPENDENT("Some placeholders may be omitted in certain locales"), //
18         MULTIPLE(
19                 "May have multiple instances of the same placeholder, eg “{0} cats and {0} dogs”."), //
20         OPTIONAL(
21                 "Any specific placeholder is optional (and non-numeric); there must be at least one.") //
22     ;
23 
24         private final String message;
25 
PlaceholderStatus(String message)26         private PlaceholderStatus(String message) {
27             this.message = message;
28         }
29 
30         @Override
toString()31         public String toString() {
32             return name() + ": " + message;
33         }
34     }
35 
36     public static final ImmutableSet<Subtype> PLACEHOLDER_SUBTYPES =
37             ImmutableSet.of(
38                     Subtype.gapsInPlaceholderNumbers,
39                     Subtype.duplicatePlaceholders,
40                     Subtype.missingPlaceholders,
41                     Subtype.extraPlaceholders);
42 
43     private static class PlaceholderData {
44         PlaceholderStatus status = PlaceholderStatus.REQUIRED;
45         Map<String, PlaceholderInfo> data = new LinkedHashMap<>();
46 
add(String id, String name, String example)47         public void add(String id, String name, String example) {
48             PlaceholderInfo row = new PlaceholderInfo(name, example);
49             data.put(id, row);
50         }
51     }
52 
53     public static class PlaceholderInfo {
54         public String name;
55         public String example;
56 
PlaceholderInfo(String name, String example)57         private PlaceholderInfo(String name, String example) {
58             this.name = name;
59             this.example = example;
60         }
61 
62         @Override
toString()63         public String toString() {
64             return "{" + name + "}, e.g. “" + example + "”";
65         }
66     }
67 
68     private static final class MyMerger implements Merger<PlaceholderData> {
69         @Override
merge(PlaceholderData a, PlaceholderData into)70         public PlaceholderData merge(PlaceholderData a, PlaceholderData into) {
71             // check unique
72             for (String key : a.data.keySet()) {
73                 if (into.data.containsKey(key)) {
74                     throw new IllegalArgumentException("Duplicate placeholder: " + key);
75                 }
76             }
77             into.data.putAll(a.data);
78             if (into.status != a.status) {
79                 throw new IllegalArgumentException("Different optional status");
80             }
81             return into;
82         }
83     }
84 
85     private static final class MapTransform implements Transform<String, PlaceholderData> {
86 
87         @Override
transform(String source)88         public PlaceholderData transform(String source) {
89             PlaceholderData result = new PlaceholderData();
90             try {
91                 String[] parts = source.split("\\s*;\\s+");
92                 for (String part : parts) {
93                     switch (part) {
94                         case "locale":
95                             result.status = PlaceholderStatus.LOCALE_DEPENDENT;
96                             continue;
97                         case "multiple":
98                             result.status = PlaceholderStatus.MULTIPLE;
99                             continue;
100                         case "optional":
101                             result.status = PlaceholderStatus.OPTIONAL;
102                             continue;
103                         default:
104                             int equalsPos = part.indexOf('=');
105                             String id = part.substring(0, equalsPos).trim();
106                             String name = part.substring(equalsPos + 1).trim();
107                             int spacePos = name.indexOf(' ');
108                             String example;
109                             if (spacePos >= 0) {
110                                 example = name.substring(spacePos + 1).trim();
111                                 name = name.substring(0, spacePos).trim();
112                             } else {
113                                 example = "";
114                             }
115                             // TODO Some normalization of personName namePattern placeholder ids.
116                             // Hack for now, later do something maybe using
117                             // PersonNameFormatter.ModifiedField
118                             id = id.replace("-allCaps", "");
119                             id = id.replace("-initialCap", "");
120                             id = id.replace("-initial", "");
121                             id = id.replace("-monogram", "");
122 
123                             PlaceholderInfo old = result.data.get(id);
124                             if (old != null) {
125                                 throw new IllegalArgumentException(
126                                         "Key occurs twice: " + id + "=" + old + "!=" + name);
127                             }
128                             result.add(id, name, example);
129                     }
130                 }
131             } catch (Exception e) {
132                 throw new IllegalArgumentException("Failed to parse " + source, e);
133             }
134             return result;
135         }
136     }
137 
138     private final RegexLookup<PlaceholderData> patternPlaceholders;
139 
PatternPlaceholders()140     private PatternPlaceholders() {
141         patternPlaceholders =
142                 RegexLookup.of(new MapTransform())
143                         .setValueMerger(new MyMerger())
144                         .loadFromFile(PatternPlaceholders.class, "data/Placeholders.txt");
145     }
146 
147     private static final class PatternPlaceholdersHelper {
148         static final PatternPlaceholders SINGLETON = new PatternPlaceholders();
149     }
150 
getInstance()151     public static PatternPlaceholders getInstance() {
152         return PatternPlaceholdersHelper.SINGLETON;
153     }
154 
get(String path)155     public Map<String, PlaceholderInfo> get(String path) {
156         // TODO change the original map to be unmodifiable, to avoid this step. Need to add a
157         // "finalize" to the lookup.
158         final PlaceholderData map = patternPlaceholders.get(path);
159         return map == null ? ImmutableMap.of() : Collections.unmodifiableMap(map.data);
160     }
161 
getStatus(String path)162     public PlaceholderStatus getStatus(String path) {
163         // TODO change the original map to be unmodifiable, to avoid this step. Need to add a
164         // "finalize" to the lookup.
165         final PlaceholderData map = patternPlaceholders.get(path);
166         return map == null ? PlaceholderStatus.DISALLOWED : map.status;
167     }
168 }
169