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