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("abc\uD800\uDC00")) { 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("abc\uD800\uDC00")) { 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