1 package org.unicode.cldr.util;
2 
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.Iterator;
7 import java.util.List;
8 
9 import com.ibm.icu.lang.CharSequences;
10 import com.ibm.icu.text.Transform;
11 import com.ibm.icu.text.UTF16;
12 
13 /**
14  * Simple cover class for converting iterators, lists of items, arrays, and
15  * CharSequences into forms usable with for loops. Example:
16  *
17  * <pre>
18  * for (String s : With.in(someIterator)) {
19  *     doSomethingWith(s);
20  * }
21  *
22  * for (int codePoint : With.codePointArray(&quot;abc\uD800\uDC00&quot;)) {
23  *     doSomethingWith(codePoint);
24  * }
25  *
26  * for (int integer : With.array(1, 99, 3, 42)) {
27  *     doSomethingWith(integer);
28  * }
29  * </pre>
30  *
31  * @author markdavis
32  *
33  * @param <V>
34  */
35 public final class With<V> implements Iterable<V>, Iterator<V> {
36     List<Iterator<V>> iterators = new ArrayList<>();
37     int current;
38 
39     /**
40      * Interface for an iterator that is simpler to implement, without 'look-ahead'.
41      * Using With.in(), this can be transformed into a regular Java iterator.
42      * The one restriction is that elements cannot be null, since that signals end of the sequence.
43      *
44      * @author markdavis
45      *
46      * @param <T>
47      */
48     public interface SimpleIterator<T> {
49         /**
50          * Returns null when done
51          *
52          * @return object, or null when done.
53          */
next()54         public T next();
55     }
56 
57     @Override
iterator()58     public Iterator<V> iterator() {
59         return this;
60     }
61 
62     @Override
hasNext()63     public boolean hasNext() {
64         while (current < iterators.size()) {
65             if (iterators.get(current).hasNext()) {
66                 return true;
67             }
68             current++;
69         }
70         return false;
71     }
72 
73     @Override
next()74     public V next() {
75         return iterators.get(current).next();
76     }
77 
78     @Override
remove()79     public void remove() {
80         throw new UnsupportedOperationException();
81     }
82 
83     /**
84      * Create a collection from whatever is left in the iterator. For example, myCollection =
85      * With.in(anIterator).toList();
86      *
87      * @return
88      */
toList()89     public List<V> toList() {
90         return toCollection(new ArrayList<V>());
91     }
92 
93     /**
94      * Create a collection from whatever is left in the iterator. For example, myCollection =
95      * With.in(anIterator).toList();
96      *
97      * @return
98      */
toCollection(C output)99     public <C extends Collection<V>> C toCollection(C output) {
100         while (hasNext()) {
101             output.add(next());
102         }
103         return output;
104     }
105 
106     /**
107      * Create a collection from whatever is left in the iterator. For example, myCollection =
108      * With.in(anIterator).toList();
109      *
110      * @return
111      */
toUnmodifiableCollection(C output)112     public <C extends Collection<V>> C toUnmodifiableCollection(C output) {
113         while (hasNext()) {
114             output.add(next());
115         }
116         return CldrUtility.protectCollection(output);
117     }
118 
119     /**
120      * Create a collection from whatever is left in the iterator. For example, myCollection =
121      * With.in(anIterator).toList();
122      *
123      * @return
124      */
toCollection(Transform<V, W> filter, C output)125     public <W, C extends Collection<W>> C toCollection(Transform<V, W> filter, C output) {
126         while (hasNext()) {
127             W transformedItem = filter.transform(next());
128             if (transformedItem != null) {
129                 output.add(transformedItem);
130             }
131         }
132         return output;
133     }
134 
135     /**
136      * Create an immutable collection from whatever is left in the iterator. For example, myCollection =
137      * With.in(anIterator).toList();
138      *
139      * @return
140      */
toUnmodifiableCollection(Transform<V, W> filter, C output)141     public <W, C extends Collection<W>> C toUnmodifiableCollection(Transform<V, W> filter, C output) {
142         return CldrUtility.protectCollection(toCollection(filter, output));
143     }
144 
145     /**
146      * Create a simple object for use in for loops. Example:
147      *
148      * <pre>
149      * for (int integer : With.in(1, 99, 3, 42)) {
150      *     doSomethingWith(integer);
151      * }
152      * </pre>
153      *
154      * @param <V>
155      * @param iterator
156      * @return Iterable, for use in for loops, etc.
157      */
158     @SuppressWarnings("unchecked")
array(V... values)159     public static <V> V[] array(V... values) {
160         return values;
161     }
162 
163     /**
164      * Create a simple object for use in for loops, handling code points
165      * properly. Example:
166      *
167      * <pre>
168      * for (int codePoint : With.in(&quot;abc\uD800\uDC00&quot;)) {
169      *     doSomethingWith(codePoint);
170      * }
171      * </pre>
172      *
173      * Actually returns an array, which avoids boxing/unboxing costs.
174      *
175      * @param iterator
176      * @return Iterable, for use in for loops, etc.
177      */
codePointArray(CharSequence source)178     public static int[] codePointArray(CharSequence source) {
179         return CharSequences.codePoints(source);
180     }
181 
182     /**
183      * Create a string from a list of code points; the inverse of codePointArray.
184      * @param codePoints
185      * @return string
186      */
fromCodePoint(int... codePoints)187     public static String fromCodePoint(int... codePoints) {
188         switch (codePoints.length) {
189         case 0: return "";
190         case 1: {
191             return String.valueOf(Character.toChars(codePoints[0]));
192         }
193         default: {
194             StringBuilder b = new StringBuilder();
195             for (int cp : codePoints) {
196                 b.appendCodePoint(cp);
197             }
198             return b.toString();
199         }
200         }
201     }
202 
203     /**
204      * An alterative to With.in(CharSequence) that is better when it is likely that only a portion of the text will be
205      * looked at,
206      * such as when an iterator over codepoints is aborted partway.
207      *
208      * @param old
209      * @return
210      */
codePoints(CharSequence... charSequences)211     public static With<CharSequence> codePoints(CharSequence... charSequences) {
212         return new With<CharSequence>().andCodePoints(charSequences);
213     }
214 
215     /**
216      * Create a simple object for use in for loops. Example:
217      *
218      * <pre>
219      * for (String s : With.in(someIterator)) {
220      *     doSomethingWith(s);
221      * }
222      * </pre>
223      *
224      * @param <V>
225      * @param iterator
226      * @return Iterable, for use in for loops, etc.
227      */
228     @SafeVarargs
229     @SuppressWarnings("unchecked")
in(Iterator<V>.... iterators)230     public static <V> With<V> in(Iterator<V>... iterators) {
231         return new With<V>().and(iterators);
232     }
233 
234     /**
235      * Create a simple object for use in for loops. Example:
236      *
237      * <pre>
238      * for (String s : With.in(someIterator)) {
239      *     doSomethingWith(s);
240      * }
241      * </pre>
242      *
243      * @param <V>
244      * @param iterator
245      * @return Iterable, for use in for loops, etc.
246      */
247     @SuppressWarnings("unchecked")
in(Iterable<V>.... iterables)248     public static <V> With<V> in(Iterable<V>... iterables) {
249         return new With<V>().and(iterables);
250     }
251 
252     /**
253      * Create a simple object for use in for loops. Example:
254      *
255      * <pre>
256      * for (String s : With.in(someIterator)) {
257      *     doSomethingWith(s);
258      * }
259      * </pre>
260      *
261      * @param <V>
262      * @param iterator
263      * @return Iterable, for use in for loops, etc.
264      */
265     @SuppressWarnings("unchecked")
in(V... items)266     public static <V> With<V> in(V... items) {
267         return new With<V>().and(items);
268     }
269 
270     /**
271      * Creates an iterable from a simple iterator.
272      *
273      * @param <T>
274      * @param old
275      * @return
276      */
277     @SuppressWarnings("unchecked")
in(SimpleIterator<T>.... sources)278     public static <T> Iterable<T> in(SimpleIterator<T>... sources) {
279         return new With<T>().and(sources);
280     }
281 
With()282     private With() {
283     }
284 
285     @SuppressWarnings("unchecked")
and(Iterator<V>.... iterators)286     public With<V> and(Iterator<V>... iterators) {
287         for (Iterator<V> iterator : iterators) {
288             this.iterators.add(iterator);
289         }
290         return this;
291     }
292 
293     @SuppressWarnings("unchecked")
and(V... items)294     public With<V> and(V... items) {
295         return and(Arrays.asList(items));
296     }
297 
298     @SuppressWarnings("unchecked")
and(Iterable<V>.... iterables)299     public With<V> and(Iterable<V>... iterables) {
300         for (Iterable<V> iterable : iterables) {
301             this.iterators.add(iterable.iterator());
302         }
303         return this;
304     }
305 
306     @SuppressWarnings("unchecked")
and(SimpleIterator<V>.... iterators)307     public With<V> and(SimpleIterator<V>... iterators) {
308         for (SimpleIterator<V> iterator : iterators) {
309             this.iterators.add(new ToIterator<>(iterator));
310         }
311         return this;
312     }
313 
314     /**
315      * Will fail if V is not a CharSequence.
316      *
317      * @param sources
318      * @return
319      */
andCodePoints(CharSequence... sources)320     public With<V> andCodePoints(CharSequence... sources) {
321         for (CharSequence charSequence : sources) {
322             this.iterators
323             .add((Iterator<V>) new ToIterator<>(new CharSequenceSimpleIterator(charSequence)));
324         }
325         return this;
326     }
327 
328     // new CharSequenceSimpleIterator(source)
329 
330     private static class CharSequenceSimpleIterator implements SimpleIterator<CharSequence> {
331         private int position;
332         private CharSequence source;
333 
CharSequenceSimpleIterator(CharSequence source)334         public CharSequenceSimpleIterator(CharSequence source) {
335             this.source = source;
336         }
337 
338         @Override
next()339         public CharSequence next() {
340             // TODO optimize
341             if (position >= source.length()) {
342                 return null;
343             }
344             int codePoint = Character.codePointAt(source, position);
345             position += Character.charCount(codePoint);
346             return UTF16.valueOf(codePoint);
347         }
348     }
349 
toIterator(SimpleIterator<T> simple)350     public static <T> Iterator<T> toIterator(SimpleIterator<T> simple) {
351         return new ToIterator<>(simple);
352     }
353 
toIterable(SimpleIterator<T> simple)354     public static <T> Iterable<T> toIterable(SimpleIterator<T> simple) {
355         return new ToIterator<>(simple);
356     }
357 
toSimpleIterator(Iterator<T> iterator)358     public static <T> ToSimpleIterator<T> toSimpleIterator(Iterator<T> iterator) {
359         return new ToSimpleIterator<>(iterator);
360     }
361 
362     private static class ToIterator<T> implements Iterator<T>, Iterable<T> {
363         private final SimpleIterator<T> simpleIterator;
364         private T current;
365 
366         /**
367          * @param simpleIterator
368          */
ToIterator(SimpleIterator<T> simpleIterator)369         public ToIterator(SimpleIterator<T> simpleIterator) {
370             this.simpleIterator = simpleIterator;
371             current = simpleIterator.next();
372         }
373 
374         @Override
hasNext()375         public boolean hasNext() {
376             return current != null;
377         }
378 
379         @Override
next()380         public T next() {
381             T result = current;
382             current = simpleIterator.next();
383             return result;
384         }
385 
386         @Override
remove()387         public void remove() {
388             throw new UnsupportedOperationException();
389         }
390 
391         @Override
iterator()392         public Iterator<T> iterator() {
393             return this;
394         }
395     }
396 
397     public static class ToSimpleIterator<T> implements SimpleIterator<T> {
398         private final Iterator<T> iterator;
399 
ToSimpleIterator(Iterator<T> iterator)400         public ToSimpleIterator(Iterator<T> iterator) {
401             this.iterator = iterator;
402         }
403 
404         @Override
next()405         public T next() {
406             return iterator.hasNext() ? iterator.next() : null;
407         }
408     }
409 }
410