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