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