1 // © 2017 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 package com.ibm.icu.impl.locale; 4 5 import java.io.BufferedReader; 6 import java.io.File; 7 import java.io.InputStream; 8 import java.io.InputStreamReader; 9 import java.net.URL; 10 import java.nio.charset.Charset; 11 import java.util.Arrays; 12 import java.util.Collection; 13 import java.util.Collections; 14 import java.util.HashMap; 15 import java.util.HashSet; 16 import java.util.Iterator; 17 import java.util.LinkedHashMap; 18 import java.util.LinkedHashSet; 19 import java.util.List; 20 import java.util.Map; 21 import java.util.Map.Entry; 22 import java.util.Set; 23 import java.util.TreeMap; 24 import java.util.TreeSet; 25 import java.util.regex.Matcher; 26 import java.util.regex.Pattern; 27 28 import com.ibm.icu.util.ICUException; 29 import com.ibm.icu.util.ICUUncheckedIOException; 30 31 /** 32 * Stub class to make migration easier until we get either Guava or a higher level of Java. 33 */ 34 public class XCldrStub { 35 36 public static class Multimap<K, V> { 37 private final Map<K,Set<V>> map; 38 private final Class<Set<V>> setClass; 39 40 @SuppressWarnings("unchecked") Multimap(Map<K,Set<V>> map, Class<?> setClass)41 private Multimap(Map<K,Set<V>> map, Class<?> setClass) { 42 this.map = map; 43 this.setClass = (Class<Set<V>>) (setClass != null 44 ? setClass 45 : HashSet.class); 46 } 47 @SafeVarargs 48 @SuppressWarnings("varargs") // Not supported by Eclipse, but we need this for javac putAll(K key, V... values)49 public final Multimap<K, V> putAll(K key, V... values) { 50 if (values.length != 0) { 51 createSetIfMissing(key).addAll(Arrays.asList(values)); 52 } 53 return this; 54 } putAll(K key, Collection<V> values)55 public void putAll(K key, Collection<V> values) { 56 if (!values.isEmpty()) { 57 createSetIfMissing(key).addAll(values); 58 } 59 } putAll(Collection<K> keys, V value)60 public void putAll(Collection<K> keys, V value) { 61 for (K key : keys) { 62 put(key, value); 63 } 64 } putAll(Multimap<K, V> source)65 public void putAll(Multimap<K, V> source) { 66 for (Entry<K, Set<V>> entry : source.map.entrySet()) { 67 putAll(entry.getKey(), entry.getValue()); 68 } 69 } put(K key, V value)70 public void put(K key, V value) { 71 createSetIfMissing(key).add(value); 72 } createSetIfMissing(K key)73 private Set<V> createSetIfMissing(K key) { 74 Set<V> old = map.get(key); 75 if (old == null) { 76 map.put(key, old = getInstance()); 77 } 78 return old; 79 } getInstance()80 private Set<V> getInstance() { 81 try { 82 return setClass.newInstance(); 83 } catch (Exception e) { 84 throw new ICUException(e); 85 } 86 } get(K key)87 public Set<V> get(K key) { 88 Set<V> result = map.get(key); 89 return result; // == null ? Collections.<V>emptySet() : result; 90 } keySet()91 public Set<K> keySet() { 92 return map.keySet(); 93 } asMap()94 public Map<K, Set<V>> asMap() { 95 return map; 96 } values()97 public Set<V> values() { 98 Collection<Set<V>> values = map.values(); 99 if (values.size() == 0) { 100 return Collections.<V>emptySet(); 101 } 102 Set<V> result = getInstance(); 103 for ( Set<V> valueSet : values) { 104 result.addAll(valueSet); 105 } 106 return result; 107 } size()108 public int size() { 109 return map.size(); 110 } entries()111 public Iterable<Entry<K, V>> entries() { 112 return new MultimapIterator<>(map); 113 } 114 @Override equals(Object obj)115 public boolean equals(Object obj) { 116 return this == obj || 117 (obj != null 118 && obj.getClass() == this.getClass() 119 && map.equals(((Multimap<?,?>) obj).map)); 120 } 121 122 @Override hashCode()123 public int hashCode() { 124 return map.hashCode(); 125 } 126 } 127 128 public static class Multimaps { invertFrom(Multimap<V, K> source, R target)129 public static <K, V, R extends Multimap<K, V>> R invertFrom(Multimap<V, K> source, R target) { 130 for (Entry<V, Set<K>> entry : source.asMap().entrySet()) { 131 target.putAll(entry.getValue(), entry.getKey()); 132 } 133 return target; 134 } invertFrom(Map<V, K> source, R target)135 public static <K, V, R extends Multimap<K, V>> R invertFrom(Map<V, K> source, R target) { 136 for (Entry<V, K> entry : source.entrySet()) { 137 target.put(entry.getValue(), entry.getKey()); 138 } 139 return target; 140 } 141 /** 142 * Warning, not functionally the same as Guava; only for use in invertFrom. 143 */ forMap(Map<K,V> map)144 public static <K, V> Map<K,V> forMap(Map<K,V> map) { 145 return map; 146 } 147 } 148 149 private static class MultimapIterator<K,V> implements Iterator<Entry<K,V>>, Iterable<Entry<K,V>> { 150 private final Iterator<Entry<K, Set<V>>> it1; 151 private Iterator<V> it2 = null; 152 private final ReusableEntry<K,V> entry = new ReusableEntry<>(); 153 MultimapIterator(Map<K,Set<V>> map)154 private MultimapIterator(Map<K,Set<V>> map) { 155 it1 = map.entrySet().iterator(); 156 } 157 @Override hasNext()158 public boolean hasNext() { 159 return it1.hasNext() || it2 != null && it2.hasNext(); 160 } 161 @Override next()162 public Entry<K, V> next() { 163 if (it2 != null && it2.hasNext()) { 164 entry.value = it2.next(); 165 } else { 166 Entry<K, Set<V>> e = it1.next(); 167 entry.key = e.getKey(); 168 it2 = e.getValue().iterator(); 169 } 170 return entry; 171 } 172 @Override iterator()173 public Iterator<Entry<K, V>> iterator() { 174 return this; 175 } 176 @Override remove()177 public void remove() { 178 throw new UnsupportedOperationException(); 179 } 180 } 181 182 private static class ReusableEntry<K,V> implements Entry<K,V> { 183 K key; 184 V value; 185 @Override getKey()186 public K getKey() { 187 return key; 188 } 189 @Override getValue()190 public V getValue() { 191 return value; 192 } 193 @Override setValue(V value)194 public V setValue(V value) { 195 throw new UnsupportedOperationException(); 196 } 197 } 198 199 public static class HashMultimap<K, V> extends Multimap<K, V> { HashMultimap()200 private HashMultimap() { 201 super(new HashMap<K, Set<V>>(), HashSet.class); 202 } create()203 public static <K, V> HashMultimap<K, V> create() { 204 return new HashMultimap<>(); 205 } 206 } 207 208 public static class TreeMultimap<K, V> extends Multimap<K, V> { TreeMultimap()209 private TreeMultimap() { 210 super(new TreeMap<K, Set<V>>(), TreeSet.class); 211 } create()212 public static <K, V> TreeMultimap<K, V> create() { 213 return new TreeMultimap<>(); 214 } 215 } 216 217 public static class LinkedHashMultimap<K, V> extends Multimap<K, V> { LinkedHashMultimap()218 private LinkedHashMultimap() { 219 super(new LinkedHashMap<K, Set<V>>(), LinkedHashSet.class); 220 } create()221 public static <K, V> LinkedHashMultimap<K, V> create() { 222 return new LinkedHashMultimap<>(); 223 } 224 } 225 226 227 // public static class Counter<T> implements Iterable<T>{ 228 // private Map<T,Long> data; 229 // @Override 230 // public Iterator<T> iterator() { 231 // return data.keySet().iterator(); 232 // } 233 // public long get(T s) { 234 // Long result = data.get(s); 235 // return result != null ? result : 0L; 236 // } 237 // public void add(T item, int count) { 238 // Long result = data.get(item); 239 // data.put(item, result == null ? count : result + count); 240 // } 241 // } 242 join(T[] source, String separator)243 public static <T> String join(T[] source, String separator) { 244 StringBuilder result = new StringBuilder(); 245 for (int i = 0; i < source.length; ++i) { 246 if (i != 0) result.append(separator); 247 result.append(source[i]); 248 } 249 return result.toString(); 250 } 251 join(Iterable<T> source, String separator)252 public static <T> String join(Iterable<T> source, String separator) { 253 StringBuilder result = new StringBuilder(); 254 boolean first = true; 255 for (T item : source) { 256 if (!first) result.append(separator); 257 else first = false; 258 result.append(item.toString()); 259 } 260 return result.toString(); 261 } 262 263 public static class CollectionUtilities { join(U source, String separator)264 public static <T, U extends Iterable<T>> String join(U source, String separator) { 265 return XCldrStub.join(source, separator); 266 } 267 } 268 269 public static class Joiner { 270 private final String separator; Joiner(String separator)271 private Joiner(String separator) { 272 this.separator = separator; 273 } on(String separator)274 public static final Joiner on(String separator) { 275 return new Joiner(separator); 276 } join(T[] source)277 public <T> String join(T[] source) { 278 return XCldrStub.join(source, separator); 279 } join(Iterable<T> source)280 public <T> String join(Iterable<T> source) { 281 return XCldrStub.join(source, separator); 282 } 283 } 284 285 public static class Splitter { 286 Pattern pattern; 287 boolean trimResults = false; Splitter(char c)288 public Splitter(char c) { 289 this(Pattern.compile("\\Q" + c + "\\E")); 290 } Splitter(Pattern p)291 public Splitter(Pattern p) { 292 pattern = p; 293 } on(char c)294 public static Splitter on(char c) { 295 return new Splitter(c); 296 } on(Pattern p)297 public static Splitter on(Pattern p) { 298 return new Splitter(p); 299 } splitToList(String input)300 public List<String> splitToList(String input) { 301 String[] items = pattern.split(input); 302 if (trimResults) { 303 for (int i = 0; i < items.length; ++i) { 304 items[i] = items[i].trim(); 305 } 306 } 307 return Arrays.asList(items); 308 } trimResults()309 public Splitter trimResults() { 310 trimResults = true; 311 return this; 312 } split(String input)313 public Iterable<String> split(String input) { 314 return splitToList(input); 315 } 316 } 317 318 public static class ImmutableSet { copyOf(Set<T> values)319 public static <T> Set<T> copyOf(Set<T> values) { 320 return Collections.unmodifiableSet(new LinkedHashSet<>(values)); // copy set for safety, preserve order 321 } 322 } 323 public static class ImmutableMap { copyOf(Map<K,V> values)324 public static <K,V> Map<K,V> copyOf(Map<K,V> values) { 325 return Collections.unmodifiableMap(new LinkedHashMap<>(values)); // copy set for safety, preserve order 326 } 327 } 328 public static class ImmutableMultimap { copyOf(Multimap<K,V> values)329 public static <K,V> Multimap<K,V> copyOf(Multimap<K,V> values) { 330 LinkedHashMap<K, Set<V>> temp = new LinkedHashMap<>(); // semi-deep copy, preserve order 331 for (Entry<K, Set<V>> entry : values.asMap().entrySet()) { 332 Set<V> value = entry.getValue(); 333 temp.put(entry.getKey(), value.size() == 1 334 ? Collections.singleton(value.iterator().next()) 335 : Collections.unmodifiableSet(new LinkedHashSet<>(value))); 336 } 337 return new Multimap<>(Collections.unmodifiableMap(temp), null); 338 } 339 } 340 341 public static class FileUtilities { 342 public static final Charset UTF8 = Charset.forName("utf-8"); 343 openFile(Class<?> class1, String file)344 public static BufferedReader openFile(Class<?> class1, String file) { 345 return openFile(class1, file, UTF8); 346 } 347 openFile(Class<?> class1, String file, Charset charset)348 public static BufferedReader openFile(Class<?> class1, String file, Charset charset) { 349 // URL path = null; 350 // String externalForm = null; 351 try { 352 final InputStream resourceAsStream = class1.getResourceAsStream(file); 353 if (charset == null) { 354 charset = UTF8; 355 } 356 InputStreamReader reader = new InputStreamReader(resourceAsStream, charset); 357 BufferedReader bufferedReader = new BufferedReader(reader, 1024 * 64); 358 return bufferedReader; 359 } catch (Exception e) { 360 String className = class1 == null ? null : class1.getCanonicalName(); 361 String canonicalName = null; 362 try { 363 String relativeFileName = getRelativeFileName(class1, "../util/"); 364 canonicalName = new File(relativeFileName).getCanonicalPath(); 365 } catch (Exception e1) { 366 throw new ICUUncheckedIOException("Couldn't open file: " + file + "; relative to class: " 367 + className, e); 368 } 369 throw new ICUUncheckedIOException("Couldn't open file " + file + "; in path " + canonicalName + "; relative to class: " 370 + className, e); 371 } 372 } getRelativeFileName(Class<?> class1, String filename)373 public static String getRelativeFileName(Class<?> class1, String filename) { 374 URL resource = class1 == null ? 375 FileUtilities.class.getResource(filename) : class1.getResource(filename); 376 String resourceString = resource.toString(); 377 if (resourceString.startsWith("file:")) { 378 return resourceString.substring(5); 379 } else if (resourceString.startsWith("jar:file:")) { 380 return resourceString.substring(9); 381 } else { 382 throw new ICUUncheckedIOException("File not found: " + resourceString); 383 } 384 } 385 } 386 387 static public class RegexUtilities { findMismatch(Matcher m, CharSequence s)388 public static int findMismatch(Matcher m, CharSequence s) { 389 int i; 390 for (i = 1; i < s.length(); ++i) { 391 boolean matches = m.reset(s.subSequence(0, i)).matches(); 392 if (!matches && !m.hitEnd()) { 393 break; 394 } 395 } 396 return i - 1; 397 } showMismatch(Matcher m, CharSequence s)398 public static String showMismatch(Matcher m, CharSequence s) { 399 int failPoint = findMismatch(m, s); 400 String show = s.subSequence(0, failPoint) + "☹" + s.subSequence(failPoint, s.length()); 401 return show; 402 } 403 } 404 405 public interface Predicate<T> { 406 /** 407 * Evaluates this predicate on the given argument. 408 * 409 * @param t the input argument 410 * @return {@code true} if the input argument matches the predicate, 411 * otherwise {@code false} 412 */ test(T t)413 boolean test(T t); 414 } 415 } 416