• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.util;
2 
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.regex.Pattern;
10 
11 import org.unicode.cldr.util.XPathParts.Comments;
12 
13 import com.ibm.icu.impl.Relation;
14 import com.ibm.icu.text.Normalizer2;
15 import com.ibm.icu.text.UnicodeSet;
16 import com.ibm.icu.util.VersionInfo;
17 
18 public class SimpleXMLSource extends XMLSource {
19     private Map<String, String> xpath_value = CldrUtility.newConcurrentHashMap();
20     private Map<String, String> xpath_fullXPath = CldrUtility.newConcurrentHashMap();
21     private Comments xpath_comments = new Comments(); // map from paths to comments.
22     private Relation<String, String> VALUE_TO_PATH = null;
23     private Object VALUE_TO_PATH_MUTEX = new Object();
24     private VersionInfo dtdVersionInfo;
25 
SimpleXMLSource(String localeID)26     public SimpleXMLSource(String localeID) {
27         this.setLocaleID(localeID);
28     }
29 
30     /**
31      * Create a shallow, locked copy of another XMLSource.
32      *
33      * @param copyAsLockedFrom
34      */
SimpleXMLSource(SimpleXMLSource copyAsLockedFrom)35     protected SimpleXMLSource(SimpleXMLSource copyAsLockedFrom) {
36         this.xpath_value = copyAsLockedFrom.xpath_value;
37         this.xpath_fullXPath = copyAsLockedFrom.xpath_fullXPath;
38         this.xpath_comments = copyAsLockedFrom.xpath_comments;
39         this.setLocaleID(copyAsLockedFrom.getLocaleID());
40         locked = true;
41     }
42 
getValueAtDPath(String xpath)43     public String getValueAtDPath(String xpath) {
44         return (String) xpath_value.get(xpath);
45     }
46 
getFullPathAtDPath(String xpath)47     public String getFullPathAtDPath(String xpath) {
48         String result = (String) xpath_fullXPath.get(xpath);
49         if (result != null) return result;
50         if (xpath_value.get(xpath) != null) return xpath; // we don't store duplicates
51         // System.err.println("WARNING: "+getLocaleID()+": path not present in data: " + xpath);
52         // return xpath;
53         return null; // throw new IllegalArgumentException("Path not present in data: " + xpath);
54     }
55 
getXpathComments()56     public Comments getXpathComments() {
57         return xpath_comments;
58     }
59 
setXpathComments(Comments xpath_comments)60     public void setXpathComments(Comments xpath_comments) {
61         this.xpath_comments = xpath_comments;
62     }
63 
64     // public void putPathValue(String xpath, String value) {
65     // if (locked) throw new UnsupportedOperationException("Attempt to modify locked object");
66     // String distinguishingXPath = CLDRFile.getDistinguishingXPath(xpath, fixedPath);
67     // xpath_value.put(distinguishingXPath, value);
68     // if (!fixedPath[0].equals(distinguishingXPath)) {
69     // xpath_fullXPath.put(distinguishingXPath, fixedPath[0]);
70     // }
71     // }
removeValueAtDPath(String distinguishingXPath)72     public void removeValueAtDPath(String distinguishingXPath) {
73         String oldValue = xpath_value.get(distinguishingXPath);
74         xpath_value.remove(distinguishingXPath);
75         xpath_fullXPath.remove(distinguishingXPath);
76         updateValuePathMapping(distinguishingXPath, oldValue, null);
77     }
78 
iterator()79     public Iterator<String> iterator() { // must be unmodifiable or locked
80         return Collections.unmodifiableSet(xpath_value.keySet()).iterator();
81     }
82 
freeze()83     public XMLSource freeze() {
84         locked = true;
85         return this;
86     }
87 
cloneAsThawed()88     public XMLSource cloneAsThawed() {
89         SimpleXMLSource result = (SimpleXMLSource) super.cloneAsThawed();
90         result.xpath_comments = (Comments) result.xpath_comments.clone();
91         result.xpath_fullXPath = CldrUtility.newConcurrentHashMap(result.xpath_fullXPath);
92         result.xpath_value = CldrUtility.newConcurrentHashMap(result.xpath_value);
93         return result;
94     }
95 
putFullPathAtDPath(String distinguishingXPath, String fullxpath)96     public void putFullPathAtDPath(String distinguishingXPath, String fullxpath) {
97         xpath_fullXPath.put(distinguishingXPath, fullxpath);
98     }
99 
putValueAtDPath(String distinguishingXPath, String value)100     public void putValueAtDPath(String distinguishingXPath, String value) {
101         String oldValue = xpath_value.get(distinguishingXPath);
102         xpath_value.put(distinguishingXPath, value);
103         updateValuePathMapping(distinguishingXPath, oldValue, value);
104     }
105 
updateValuePathMapping(String distinguishingXPath, String oldValue, String newValue)106     private void updateValuePathMapping(String distinguishingXPath, String oldValue, String newValue) {
107         synchronized (VALUE_TO_PATH_MUTEX) {
108             if (VALUE_TO_PATH != null) {
109                 if (oldValue != null) {
110                     VALUE_TO_PATH.remove(normalize(oldValue), distinguishingXPath);
111                 }
112                 if (newValue != null) {
113                     VALUE_TO_PATH.put(normalize(newValue), distinguishingXPath);
114                 }
115             }
116         }
117     }
118 
119     @Override
getPathsWithValue(String valueToMatch, String pathPrefix, Set<String> result)120     public void getPathsWithValue(String valueToMatch, String pathPrefix, Set<String> result) {
121         // build a Relation mapping value to paths, if needed
122         synchronized (VALUE_TO_PATH_MUTEX) {
123             if (VALUE_TO_PATH == null) {
124                 VALUE_TO_PATH = Relation.of(new HashMap<String, Set<String>>(), HashSet.class);
125                 for (Iterator<String> it = iterator(); it.hasNext();) {
126                     String path = it.next();
127                     String value = normalize(getValueAtDPath(path));
128                     VALUE_TO_PATH.put(value, path);
129                 }
130             }
131             Set<String> paths = VALUE_TO_PATH.getAll(normalize(valueToMatch));
132             if (paths == null) {
133                 return;
134             }
135             if (pathPrefix == null || pathPrefix.length() == 0) {
136                 result.addAll(paths);
137                 return;
138             }
139             for (String path : paths) {
140                 if (path.startsWith(pathPrefix)) {
141                     // if (altPath.originalPath.startsWith(altPrefix.originalPath)) {
142                     result.add(path);
143                 }
144             }
145         }
146     }
147 
148     static final Normalizer2 NFKCCF = Normalizer2.getNFKCCasefoldInstance();
149     static final Normalizer2 NFKC = Normalizer2.getNFKCInstance();
150 
151     // The following includes letters, marks, numbers, currencies, and *selected* symbols/punctuation
152     static final UnicodeSet NON_ALPHANUM = new UnicodeSet("[^[:L:][:M:][:N:][:Sc:]/+\\-°′″%‰٪؉−⍰()]").freeze();
153 
normalize(String valueToMatch)154     public static String normalize(String valueToMatch) {
155         return replace(NON_ALPHANUM, NFKCCF.normalize(valueToMatch), "");
156     }
157 
normalizeCaseSensitive(String valueToMatch)158     public static String normalizeCaseSensitive(String valueToMatch) {
159         return replace(NON_ALPHANUM, NFKC.normalize(valueToMatch), "");
160     }
161 
replace(UnicodeSet unicodeSet, String valueToMatch, String substitute)162     public static String replace(UnicodeSet unicodeSet, String valueToMatch, String substitute) {
163         // handle patterns
164         if (valueToMatch.contains("{")) {
165             valueToMatch = PLACEHOLDER.matcher(valueToMatch).replaceAll("⍰").trim();
166         }
167         StringBuilder b = null; // delay creating until needed
168         for (int i = 0; i < valueToMatch.length(); ++i) {
169             int cp = valueToMatch.codePointAt(i);
170             if (unicodeSet.contains(cp)) {
171                 if (b == null) {
172                     b = new StringBuilder();
173                     b.append(valueToMatch.substring(0, i)); // copy the start
174                 }
175                 if (substitute.length() != 0) {
176                     b.append(substitute);
177                 }
178             } else if (b != null) {
179                 b.appendCodePoint(cp);
180             }
181             if (cp > 0xFFFF) { // skip end of supplemental character
182                 ++i;
183             }
184         }
185         if (b != null) {
186             valueToMatch = b.toString();
187         }
188         return valueToMatch;
189     }
190 
191     static final Pattern PLACEHOLDER = PatternCache.get("\\{\\d\\}");
192 
setDtdVersionInfo(VersionInfo dtdVersionInfo)193     public void setDtdVersionInfo(VersionInfo dtdVersionInfo) {
194         this.dtdVersionInfo = dtdVersionInfo;
195     }
196 
getDtdVersionInfo()197     public VersionInfo getDtdVersionInfo() {
198         return dtdVersionInfo;
199     }
200 }