| // © 2017 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html#License |
| package com.ibm.icu.impl.locale; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.net.URL; |
| import java.nio.charset.Charset; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import com.ibm.icu.util.ICUException; |
| import com.ibm.icu.util.ICUUncheckedIOException; |
| |
| /** |
| * Stub class to make migration easier until we get either Guava or a higher level of Java. |
| */ |
| public class XCldrStub { |
| |
| public static class Multimap<K, V> { |
| private final Map<K,Set<V>> map; |
| private final Class<Set<V>> setClass; |
| |
| @SuppressWarnings("unchecked") |
| private Multimap(Map<K,Set<V>> map, Class<?> setClass) { |
| this.map = map; |
| this.setClass = (Class<Set<V>>) (setClass != null |
| ? setClass |
| : HashSet.class); |
| } |
| @SafeVarargs |
| @SuppressWarnings("varargs") // Not supported by Eclipse, but we need this for javac |
| public final Multimap<K, V> putAll(K key, V... values) { |
| if (values.length != 0) { |
| createSetIfMissing(key).addAll(Arrays.asList(values)); |
| } |
| return this; |
| } |
| public void putAll(K key, Collection<V> values) { |
| if (!values.isEmpty()) { |
| createSetIfMissing(key).addAll(values); |
| } |
| } |
| public void putAll(Collection<K> keys, V value) { |
| for (K key : keys) { |
| put(key, value); |
| } |
| } |
| public void putAll(Multimap<K, V> source) { |
| for (Entry<K, Set<V>> entry : source.map.entrySet()) { |
| putAll(entry.getKey(), entry.getValue()); |
| } |
| } |
| public void put(K key, V value) { |
| createSetIfMissing(key).add(value); |
| } |
| private Set<V> createSetIfMissing(K key) { |
| Set<V> old = map.get(key); |
| if (old == null) { |
| map.put(key, old = getInstance()); |
| } |
| return old; |
| } |
| private Set<V> getInstance() { |
| try { |
| return setClass.newInstance(); |
| } catch (Exception e) { |
| throw new ICUException(e); |
| } |
| } |
| public Set<V> get(K key) { |
| Set<V> result = map.get(key); |
| return result; // == null ? Collections.<V>emptySet() : result; |
| } |
| public Set<K> keySet() { |
| return map.keySet(); |
| } |
| public Map<K, Set<V>> asMap() { |
| return map; |
| } |
| public Set<V> values() { |
| Collection<Set<V>> values = map.values(); |
| if (values.size() == 0) { |
| return Collections.<V>emptySet(); |
| } |
| Set<V> result = getInstance(); |
| for ( Set<V> valueSet : values) { |
| result.addAll(valueSet); |
| } |
| return result; |
| } |
| public int size() { |
| return map.size(); |
| } |
| public Iterable<Entry<K, V>> entries() { |
| return new MultimapIterator<>(map); |
| } |
| @Override |
| public boolean equals(Object obj) { |
| return this == obj || |
| (obj != null |
| && obj.getClass() == this.getClass() |
| && map.equals(((Multimap<?,?>) obj).map)); |
| } |
| |
| @Override |
| public int hashCode() { |
| return map.hashCode(); |
| } |
| } |
| |
| public static class Multimaps { |
| public static <K, V, R extends Multimap<K, V>> R invertFrom(Multimap<V, K> source, R target) { |
| for (Entry<V, Set<K>> entry : source.asMap().entrySet()) { |
| target.putAll(entry.getValue(), entry.getKey()); |
| } |
| return target; |
| } |
| public static <K, V, R extends Multimap<K, V>> R invertFrom(Map<V, K> source, R target) { |
| for (Entry<V, K> entry : source.entrySet()) { |
| target.put(entry.getValue(), entry.getKey()); |
| } |
| return target; |
| } |
| /** |
| * Warning, not functionally the same as Guava; only for use in invertFrom. |
| */ |
| public static <K, V> Map<K,V> forMap(Map<K,V> map) { |
| return map; |
| } |
| } |
| |
| private static class MultimapIterator<K,V> implements Iterator<Entry<K,V>>, Iterable<Entry<K,V>> { |
| private final Iterator<Entry<K, Set<V>>> it1; |
| private Iterator<V> it2 = null; |
| private final ReusableEntry<K,V> entry = new ReusableEntry<>(); |
| |
| private MultimapIterator(Map<K,Set<V>> map) { |
| it1 = map.entrySet().iterator(); |
| } |
| @Override |
| public boolean hasNext() { |
| return it1.hasNext() || it2 != null && it2.hasNext(); |
| } |
| @Override |
| public Entry<K, V> next() { |
| if (it2 != null && it2.hasNext()) { |
| entry.value = it2.next(); |
| } else { |
| Entry<K, Set<V>> e = it1.next(); |
| entry.key = e.getKey(); |
| it2 = e.getValue().iterator(); |
| } |
| return entry; |
| } |
| @Override |
| public Iterator<Entry<K, V>> iterator() { |
| return this; |
| } |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| private static class ReusableEntry<K,V> implements Entry<K,V> { |
| K key; |
| V value; |
| @Override |
| public K getKey() { |
| return key; |
| } |
| @Override |
| public V getValue() { |
| return value; |
| } |
| @Override |
| public V setValue(V value) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| public static class HashMultimap<K, V> extends Multimap<K, V> { |
| private HashMultimap() { |
| super(new HashMap<K, Set<V>>(), HashSet.class); |
| } |
| public static <K, V> HashMultimap<K, V> create() { |
| return new HashMultimap<>(); |
| } |
| } |
| |
| public static class TreeMultimap<K, V> extends Multimap<K, V> { |
| private TreeMultimap() { |
| super(new TreeMap<K, Set<V>>(), TreeSet.class); |
| } |
| public static <K, V> TreeMultimap<K, V> create() { |
| return new TreeMultimap<>(); |
| } |
| } |
| |
| public static class LinkedHashMultimap<K, V> extends Multimap<K, V> { |
| private LinkedHashMultimap() { |
| super(new LinkedHashMap<K, Set<V>>(), LinkedHashSet.class); |
| } |
| public static <K, V> LinkedHashMultimap<K, V> create() { |
| return new LinkedHashMultimap<>(); |
| } |
| } |
| |
| |
| // public static class Counter<T> implements Iterable<T>{ |
| // private Map<T,Long> data; |
| // @Override |
| // public Iterator<T> iterator() { |
| // return data.keySet().iterator(); |
| // } |
| // public long get(T s) { |
| // Long result = data.get(s); |
| // return result != null ? result : 0L; |
| // } |
| // public void add(T item, int count) { |
| // Long result = data.get(item); |
| // data.put(item, result == null ? count : result + count); |
| // } |
| // } |
| |
| public static <T> String join(T[] source, String separator) { |
| StringBuilder result = new StringBuilder(); |
| for (int i = 0; i < source.length; ++i) { |
| if (i != 0) result.append(separator); |
| result.append(source[i]); |
| } |
| return result.toString(); |
| } |
| |
| public static <T> String join(Iterable<T> source, String separator) { |
| StringBuilder result = new StringBuilder(); |
| boolean first = true; |
| for (T item : source) { |
| if (!first) result.append(separator); |
| else first = false; |
| result.append(item.toString()); |
| } |
| return result.toString(); |
| } |
| |
| public static class CollectionUtilities { |
| public static <T, U extends Iterable<T>> String join(U source, String separator) { |
| return XCldrStub.join(source, separator); |
| } |
| } |
| |
| public static class Joiner { |
| private final String separator; |
| private Joiner(String separator) { |
| this.separator = separator; |
| } |
| public static final Joiner on(String separator) { |
| return new Joiner(separator); |
| } |
| public <T> String join(T[] source) { |
| return XCldrStub.join(source, separator); |
| } |
| public <T> String join(Iterable<T> source) { |
| return XCldrStub.join(source, separator); |
| } |
| } |
| |
| public static class Splitter { |
| Pattern pattern; |
| boolean trimResults = false; |
| public Splitter(char c) { |
| this(Pattern.compile("\\Q" + c + "\\E")); |
| } |
| public Splitter(Pattern p) { |
| pattern = p; |
| } |
| public static Splitter on(char c) { |
| return new Splitter(c); |
| } |
| public static Splitter on(Pattern p) { |
| return new Splitter(p); |
| } |
| public List<String> splitToList(String input) { |
| String[] items = pattern.split(input); |
| if (trimResults) { |
| for (int i = 0; i < items.length; ++i) { |
| items[i] = items[i].trim(); |
| } |
| } |
| return Arrays.asList(items); |
| } |
| public Splitter trimResults() { |
| trimResults = true; |
| return this; |
| } |
| public Iterable<String> split(String input) { |
| return splitToList(input); |
| } |
| } |
| |
| public static class ImmutableSet { |
| public static <T> Set<T> copyOf(Set<T> values) { |
| return Collections.unmodifiableSet(new LinkedHashSet<>(values)); // copy set for safety, preserve order |
| } |
| } |
| public static class ImmutableMap { |
| public static <K,V> Map<K,V> copyOf(Map<K,V> values) { |
| return Collections.unmodifiableMap(new LinkedHashMap<>(values)); // copy set for safety, preserve order |
| } |
| } |
| public static class ImmutableMultimap { |
| public static <K,V> Multimap<K,V> copyOf(Multimap<K,V> values) { |
| LinkedHashMap<K, Set<V>> temp = new LinkedHashMap<>(); // semi-deep copy, preserve order |
| for (Entry<K, Set<V>> entry : values.asMap().entrySet()) { |
| Set<V> value = entry.getValue(); |
| temp.put(entry.getKey(), value.size() == 1 |
| ? Collections.singleton(value.iterator().next()) |
| : Collections.unmodifiableSet(new LinkedHashSet<>(value))); |
| } |
| return new Multimap<>(Collections.unmodifiableMap(temp), null); |
| } |
| } |
| |
| public static class FileUtilities { |
| public static final Charset UTF8 = Charset.forName("utf-8"); |
| |
| public static BufferedReader openFile(Class<?> class1, String file) { |
| return openFile(class1, file, UTF8); |
| } |
| |
| public static BufferedReader openFile(Class<?> class1, String file, Charset charset) { |
| // URL path = null; |
| // String externalForm = null; |
| try { |
| final InputStream resourceAsStream = class1.getResourceAsStream(file); |
| if (charset == null) { |
| charset = UTF8; |
| } |
| InputStreamReader reader = new InputStreamReader(resourceAsStream, charset); |
| BufferedReader bufferedReader = new BufferedReader(reader, 1024 * 64); |
| return bufferedReader; |
| } catch (Exception e) { |
| String className = class1 == null ? null : class1.getCanonicalName(); |
| String canonicalName = null; |
| try { |
| String relativeFileName = getRelativeFileName(class1, "../util/"); |
| canonicalName = new File(relativeFileName).getCanonicalPath(); |
| } catch (Exception e1) { |
| throw new ICUUncheckedIOException("Couldn't open file: " + file + "; relative to class: " |
| + className, e); |
| } |
| throw new ICUUncheckedIOException("Couldn't open file " + file + "; in path " + canonicalName + "; relative to class: " |
| + className, e); |
| } |
| } |
| public static String getRelativeFileName(Class<?> class1, String filename) { |
| URL resource = class1 == null ? |
| FileUtilities.class.getResource(filename) : class1.getResource(filename); |
| String resourceString = resource.toString(); |
| if (resourceString.startsWith("file:")) { |
| return resourceString.substring(5); |
| } else if (resourceString.startsWith("jar:file:")) { |
| return resourceString.substring(9); |
| } else { |
| throw new ICUUncheckedIOException("File not found: " + resourceString); |
| } |
| } |
| } |
| |
| static public class RegexUtilities { |
| public static int findMismatch(Matcher m, CharSequence s) { |
| int i; |
| for (i = 1; i < s.length(); ++i) { |
| boolean matches = m.reset(s.subSequence(0, i)).matches(); |
| if (!matches && !m.hitEnd()) { |
| break; |
| } |
| } |
| return i - 1; |
| } |
| public static String showMismatch(Matcher m, CharSequence s) { |
| int failPoint = findMismatch(m, s); |
| String show = s.subSequence(0, failPoint) + "☹" + s.subSequence(failPoint, s.length()); |
| return show; |
| } |
| } |
| |
| public interface Predicate<T> { |
| /** |
| * Evaluates this predicate on the given argument. |
| * |
| * @param t the input argument |
| * @return {@code true} if the input argument matches the predicate, |
| * otherwise {@code false} |
| */ |
| boolean test(T t); |
| } |
| } |