| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.collection; | 5 part of dart.collection; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * This [Iterable] mixin implements all [Iterable] members except `iterator`. | 8 * This [Iterable] mixin implements all [Iterable] members except `iterator`. |
| 9 * | 9 * |
| 10 * All other methods are implemented in terms of `iterator`. | 10 * All other methods are implemented in terms of `iterator`. |
| 11 */ | 11 */ |
| 12 abstract class IterableMixin<E> implements Iterable<E> { | 12 abstract class IterableMixin<E> implements Iterable<E> { |
| 13 // This class has methods copied verbatim into: | 13 // This class has methods copied verbatim into: |
| 14 // - IterableBase | 14 // - IterableBase |
| 15 // - SetMixin | 15 // - SetMixin |
| 16 // If changing a method here, also change the other copies. | 16 // If changing a method here, also change the other copies. |
| 17 | 17 |
| 18 Iterable<T> map<T>(T f(E element)) => | 18 Iterable<T> map<T>(T f(E element)) => new MappedIterable<E, T>(this, f); |
| 19 new MappedIterable<E, T>(this, f); | |
| 20 | 19 |
| 21 Iterable<E> where(bool f(E element)) => new WhereIterable<E>(this, f); | 20 Iterable<E> where(bool f(E element)) => new WhereIterable<E>(this, f); |
| 22 | 21 |
| 23 Iterable<T> expand<T>(Iterable<T> f(E element)) => | 22 Iterable<T> |
| 24 new ExpandIterable<E, T>(this, f); | 23 expand<T>(Iterable<T> f(E element)) => new ExpandIterable<E, T>(this, f); |
| 25 | 24 |
| 26 bool contains(Object element) { | 25 bool contains(Object element) { |
| 27 for (E e in this) { | 26 for (E e in this) { |
| 28 if (e == element) return true; | 27 if (e == element) return true; |
| 29 } | 28 } |
| 30 return false; | 29 return false; |
| 31 } | 30 } |
| 32 | 31 |
| 33 void forEach(void f(E element)) { | 32 void forEach(void f(E element)) { |
| 34 for (E element in this) f(element); | 33 for (E element in this) f(element); |
| 35 } | 34 } |
| 36 | 35 |
| 37 E reduce(E combine(E value, E element)) { | 36 E reduce(E combine(E value, E element)) { |
| 38 Iterator<E> iterator = this.iterator; | 37 Iterator<E> iterator = this.iterator; |
| 39 if (!iterator.moveNext()) { | 38 if (!iterator.moveNext()) { |
| 40 throw IterableElementError.noElement(); | 39 throw IterableElementError.noElement(); |
| 41 } | 40 } |
| 42 E value = iterator.current; | 41 E value = iterator.current; |
| 43 while (iterator.moveNext()) { | 42 while (iterator.moveNext()) { |
| 44 value = combine(value, iterator.current); | 43 value = combine(value, iterator.current); |
| 45 } | 44 } |
| 46 return value; | 45 return value; |
| 47 } | 46 } |
| 48 | 47 |
| 49 T fold<T>(T initialValue, | 48 T fold<T>(T initialValue, T combine(T previousValue, E element)) { |
| 50 T combine(T previousValue, E element)) { | |
| 51 var value = initialValue; | 49 var value = initialValue; |
| 52 for (E element in this) value = combine(value, element); | 50 for (E element in this) value = combine(value, element); |
| 53 return value; | 51 return value; |
| 54 } | 52 } |
| 55 | 53 |
| 56 bool every(bool f(E element)) { | 54 bool every(bool f(E element)) { |
| 57 for (E element in this) { | 55 for (E element in this) { |
| 58 if (!f(element)) return false; | 56 if (!f(element)) return false; |
| 59 } | 57 } |
| 60 return true; | 58 return true; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 78 return buffer.toString(); | 76 return buffer.toString(); |
| 79 } | 77 } |
| 80 | 78 |
| 81 bool any(bool f(E element)) { | 79 bool any(bool f(E element)) { |
| 82 for (E element in this) { | 80 for (E element in this) { |
| 83 if (f(element)) return true; | 81 if (f(element)) return true; |
| 84 } | 82 } |
| 85 return false; | 83 return false; |
| 86 } | 84 } |
| 87 | 85 |
| 88 List<E> toList({ bool growable: true }) => | 86 List<E> toList({bool growable: true}) => |
| 89 new List<E>.from(this, growable: growable); | 87 new List<E>.from(this, growable: growable); |
| 90 | 88 |
| 91 Set<E> toSet() => new Set<E>.from(this); | 89 Set<E> toSet() => new Set<E>.from(this); |
| 92 | 90 |
| 93 int get length { | 91 int get length { |
| 94 assert(this is! EfficientLengthIterable); | 92 assert(this is! EfficientLengthIterable); |
| 95 int count = 0; | 93 int count = 0; |
| 96 Iterator it = iterator; | 94 Iterator it = iterator; |
| 97 while (it.moveNext()) { | 95 while (it.moveNext()) { |
| 98 count++; | 96 count++; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 129 } | 127 } |
| 130 | 128 |
| 131 E get last { | 129 E get last { |
| 132 Iterator<E> it = iterator; | 130 Iterator<E> it = iterator; |
| 133 if (!it.moveNext()) { | 131 if (!it.moveNext()) { |
| 134 throw IterableElementError.noElement(); | 132 throw IterableElementError.noElement(); |
| 135 } | 133 } |
| 136 E result; | 134 E result; |
| 137 do { | 135 do { |
| 138 result = it.current; | 136 result = it.current; |
| 139 } while(it.moveNext()); | 137 } while (it.moveNext()); |
| 140 return result; | 138 return result; |
| 141 } | 139 } |
| 142 | 140 |
| 143 E get single { | 141 E get single { |
| 144 Iterator<E> it = iterator; | 142 Iterator<E> it = iterator; |
| 145 if (!it.moveNext()) throw IterableElementError.noElement(); | 143 if (!it.moveNext()) throw IterableElementError.noElement(); |
| 146 E result = it.current; | 144 E result = it.current; |
| 147 if (it.moveNext()) throw IterableElementError.tooMany(); | 145 if (it.moveNext()) throw IterableElementError.tooMany(); |
| 148 return result; | 146 return result; |
| 149 } | 147 } |
| 150 | 148 |
| 151 E firstWhere(bool test(E value), { E orElse() }) { | 149 E firstWhere(bool test(E value), {E orElse()}) { |
| 152 for (E element in this) { | 150 for (E element in this) { |
| 153 if (test(element)) return element; | 151 if (test(element)) return element; |
| 154 } | 152 } |
| 155 if (orElse != null) return orElse(); | 153 if (orElse != null) return orElse(); |
| 156 throw IterableElementError.noElement(); | 154 throw IterableElementError.noElement(); |
| 157 } | 155 } |
| 158 | 156 |
| 159 E lastWhere(bool test(E value), { E orElse() }) { | 157 E lastWhere(bool test(E value), {E orElse()}) { |
| 160 E result = null; | 158 E result = null; |
| 161 bool foundMatching = false; | 159 bool foundMatching = false; |
| 162 for (E element in this) { | 160 for (E element in this) { |
| 163 if (test(element)) { | 161 if (test(element)) { |
| 164 result = element; | 162 result = element; |
| 165 foundMatching = true; | 163 foundMatching = true; |
| 166 } | 164 } |
| 167 } | 165 } |
| 168 if (foundMatching) return result; | 166 if (foundMatching) return result; |
| 169 if (orElse != null) return orElse(); | 167 if (orElse != null) return orElse(); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 190 if (index is! int) throw new ArgumentError.notNull("index"); | 188 if (index is! int) throw new ArgumentError.notNull("index"); |
| 191 RangeError.checkNotNegative(index, "index"); | 189 RangeError.checkNotNegative(index, "index"); |
| 192 int elementIndex = 0; | 190 int elementIndex = 0; |
| 193 for (E element in this) { | 191 for (E element in this) { |
| 194 if (index == elementIndex) return element; | 192 if (index == elementIndex) return element; |
| 195 elementIndex++; | 193 elementIndex++; |
| 196 } | 194 } |
| 197 throw new RangeError.index(index, this, "index", null, elementIndex); | 195 throw new RangeError.index(index, this, "index", null, elementIndex); |
| 198 } | 196 } |
| 199 | 197 |
| 200 | |
| 201 String toString() => IterableBase.iterableToShortString(this, '(', ')'); | 198 String toString() => IterableBase.iterableToShortString(this, '(', ')'); |
| 202 } | 199 } |
| 203 | 200 |
| 204 /** | 201 /** |
| 205 * Base class for implementing [Iterable]. | 202 * Base class for implementing [Iterable]. |
| 206 * | 203 * |
| 207 * This class implements all methods of [Iterable] except [Iterable.iterator] | 204 * This class implements all methods of [Iterable] except [Iterable.iterator] |
| 208 * in terms of `iterator`. | 205 * in terms of `iterator`. |
| 209 */ | 206 */ |
| 210 abstract class IterableBase<E> extends Iterable<E> { | 207 abstract class IterableBase<E> extends Iterable<E> { |
| 211 const IterableBase(); | 208 const IterableBase(); |
| 212 | 209 |
| 213 /** | 210 /** |
| 214 * Convert an `Iterable` to a string like [IterableBase.toString]. | 211 * Convert an `Iterable` to a string like [IterableBase.toString]. |
| 215 * | 212 * |
| 216 * Allows using other delimiters than '(' and ')'. | 213 * Allows using other delimiters than '(' and ')'. |
| 217 * | 214 * |
| 218 * Handles circular references where converting one of the elements | 215 * Handles circular references where converting one of the elements |
| 219 * to a string ends up converting [iterable] to a string again. | 216 * to a string ends up converting [iterable] to a string again. |
| 220 */ | 217 */ |
| 221 static String iterableToShortString(Iterable iterable, | 218 static String iterableToShortString(Iterable iterable, |
| 222 [String leftDelimiter = '(', | 219 [String leftDelimiter = '(', String rightDelimiter = ')']) { |
| 223 String rightDelimiter = ')']) { | |
| 224 if (_isToStringVisiting(iterable)) { | 220 if (_isToStringVisiting(iterable)) { |
| 225 if (leftDelimiter == "(" && rightDelimiter == ")") { | 221 if (leftDelimiter == "(" && rightDelimiter == ")") { |
| 226 // Avoid creating a new string in the "common" case. | 222 // Avoid creating a new string in the "common" case. |
| 227 return "(...)"; | 223 return "(...)"; |
| 228 } | 224 } |
| 229 return "$leftDelimiter...$rightDelimiter"; | 225 return "$leftDelimiter...$rightDelimiter"; |
| 230 } | 226 } |
| 231 List parts = []; | 227 List parts = []; |
| 232 _toStringVisiting.add(iterable); | 228 _toStringVisiting.add(iterable); |
| 233 try { | 229 try { |
| 234 _iterablePartsToStrings(iterable, parts); | 230 _iterablePartsToStrings(iterable, parts); |
| 235 } finally { | 231 } finally { |
| 236 assert(identical(_toStringVisiting.last, iterable)); | 232 assert(identical(_toStringVisiting.last, iterable)); |
| 237 _toStringVisiting.removeLast(); | 233 _toStringVisiting.removeLast(); |
| 238 } | 234 } |
| 239 return (new StringBuffer(leftDelimiter) | 235 return (new StringBuffer(leftDelimiter) |
| 240 ..writeAll(parts, ", ") | 236 ..writeAll(parts, ", ") |
| 241 ..write(rightDelimiter)).toString(); | 237 ..write(rightDelimiter)) |
| 238 .toString(); |
| 242 } | 239 } |
| 243 | 240 |
| 244 /** | 241 /** |
| 245 * Converts an `Iterable` to a string. | 242 * Converts an `Iterable` to a string. |
| 246 * | 243 * |
| 247 * Converts each elements to a string, and separates the results by ", ". | 244 * Converts each elements to a string, and separates the results by ", ". |
| 248 * Then wraps the result in [leftDelimiter] and [rightDelimiter]. | 245 * Then wraps the result in [leftDelimiter] and [rightDelimiter]. |
| 249 * | 246 * |
| 250 * Unlike [iterableToShortString], this conversion doesn't omit any | 247 * Unlike [iterableToShortString], this conversion doesn't omit any |
| 251 * elements or puts any limit on the size of the result. | 248 * elements or puts any limit on the size of the result. |
| 252 * | 249 * |
| 253 * Handles circular references where converting one of the elements | 250 * Handles circular references where converting one of the elements |
| 254 * to a string ends up converting [iterable] to a string again. | 251 * to a string ends up converting [iterable] to a string again. |
| 255 */ | 252 */ |
| 256 static String iterableToFullString(Iterable iterable, | 253 static String iterableToFullString(Iterable iterable, |
| 257 [String leftDelimiter = '(', | 254 [String leftDelimiter = '(', String rightDelimiter = ')']) { |
| 258 String rightDelimiter = ')']) { | |
| 259 if (_isToStringVisiting(iterable)) { | 255 if (_isToStringVisiting(iterable)) { |
| 260 return "$leftDelimiter...$rightDelimiter"; | 256 return "$leftDelimiter...$rightDelimiter"; |
| 261 } | 257 } |
| 262 StringBuffer buffer = new StringBuffer(leftDelimiter); | 258 StringBuffer buffer = new StringBuffer(leftDelimiter); |
| 263 _toStringVisiting.add(iterable); | 259 _toStringVisiting.add(iterable); |
| 264 try { | 260 try { |
| 265 buffer.writeAll(iterable, ", "); | 261 buffer.writeAll(iterable, ", "); |
| 266 } finally { | 262 } finally { |
| 267 assert(identical(_toStringVisiting.last, iterable)); | 263 assert(identical(_toStringVisiting.last, iterable)); |
| 268 _toStringVisiting.removeLast(); | 264 _toStringVisiting.removeLast(); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 287 * Convert elments of [iterable] to strings and store them in [parts]. | 283 * Convert elments of [iterable] to strings and store them in [parts]. |
| 288 */ | 284 */ |
| 289 void _iterablePartsToStrings(Iterable iterable, List parts) { | 285 void _iterablePartsToStrings(Iterable iterable, List parts) { |
| 290 /* | 286 /* |
| 291 * This is the complicated part of [iterableToShortString]. | 287 * This is the complicated part of [iterableToShortString]. |
| 292 * It is extracted as a separate function to avoid having too much code | 288 * It is extracted as a separate function to avoid having too much code |
| 293 * inside the try/finally. | 289 * inside the try/finally. |
| 294 */ | 290 */ |
| 295 /// Try to stay below this many characters. | 291 /// Try to stay below this many characters. |
| 296 const int LENGTH_LIMIT = 80; | 292 const int LENGTH_LIMIT = 80; |
| 293 |
| 297 /// Always at least this many elements at the start. | 294 /// Always at least this many elements at the start. |
| 298 const int HEAD_COUNT = 3; | 295 const int HEAD_COUNT = 3; |
| 296 |
| 299 /// Always at least this many elements at the end. | 297 /// Always at least this many elements at the end. |
| 300 const int TAIL_COUNT = 2; | 298 const int TAIL_COUNT = 2; |
| 299 |
| 301 /// Stop iterating after this many elements. Iterables can be infinite. | 300 /// Stop iterating after this many elements. Iterables can be infinite. |
| 302 const int MAX_COUNT = 100; | 301 const int MAX_COUNT = 100; |
| 303 // Per entry length overhead. It's for ", " for all after the first entry, | 302 // Per entry length overhead. It's for ", " for all after the first entry, |
| 304 // and for "(" and ")" for the initial entry. By pure luck, that's the same | 303 // and for "(" and ")" for the initial entry. By pure luck, that's the same |
| 305 // number. | 304 // number. |
| 306 const int OVERHEAD = 2; | 305 const int OVERHEAD = 2; |
| 307 const int ELLIPSIS_SIZE = 3; // "...".length. | 306 const int ELLIPSIS_SIZE = 3; // "...".length. |
| 308 | 307 |
| 309 int length = 0; | 308 int length = 0; |
| 310 int count = 0; | 309 int count = 0; |
| 311 Iterator it = iterable.iterator; | 310 Iterator it = iterable.iterator; |
| 312 // Initial run of elements, at least HEAD_COUNT, and then continue until | 311 // Initial run of elements, at least HEAD_COUNT, and then continue until |
| 313 // passing at most LENGTH_LIMIT characters. | 312 // passing at most LENGTH_LIMIT characters. |
| 314 while (length < LENGTH_LIMIT || count < HEAD_COUNT) { | 313 while (length < LENGTH_LIMIT || count < HEAD_COUNT) { |
| 315 if (!it.moveNext()) return; | 314 if (!it.moveNext()) return; |
| 316 String next = "${it.current}"; | 315 String next = "${it.current}"; |
| 317 parts.add(next); | 316 parts.add(next); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 count++; | 351 count++; |
| 353 if (count > MAX_COUNT) { | 352 if (count > MAX_COUNT) { |
| 354 // If we haven't found the end before MAX_COUNT, give up. | 353 // If we haven't found the end before MAX_COUNT, give up. |
| 355 // This cannot happen in the code above because each entry | 354 // This cannot happen in the code above because each entry |
| 356 // increases length by at least two, so there is no way to | 355 // increases length by at least two, so there is no way to |
| 357 // visit more than ~40 elements before this loop. | 356 // visit more than ~40 elements before this loop. |
| 358 | 357 |
| 359 // Remove any surplus elements until length, including ", ...)", | 358 // Remove any surplus elements until length, including ", ...)", |
| 360 // is at most LENGTH_LIMIT. | 359 // is at most LENGTH_LIMIT. |
| 361 while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD && | 360 while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD && |
| 362 count > HEAD_COUNT) { | 361 count > HEAD_COUNT) { |
| 363 length -= parts.removeLast().length + OVERHEAD; | 362 length -= parts.removeLast().length + OVERHEAD; |
| 364 count--; | 363 count--; |
| 365 } | 364 } |
| 366 parts.add("..."); | 365 parts.add("..."); |
| 367 return; | 366 return; |
| 368 } | 367 } |
| 369 } | 368 } |
| 370 penultimateString = "$penultimate"; | 369 penultimateString = "$penultimate"; |
| 371 ultimateString = "$ultimate"; | 370 ultimateString = "$ultimate"; |
| 372 length += | 371 length += ultimateString.length + penultimateString.length + 2 * OVERHEAD; |
| 373 ultimateString.length + penultimateString.length + 2 * OVERHEAD; | |
| 374 } | 372 } |
| 375 } | 373 } |
| 376 | 374 |
| 377 // If there is a gap between the initial run and the last two, | 375 // If there is a gap between the initial run and the last two, |
| 378 // prepare to add an ellipsis. | 376 // prepare to add an ellipsis. |
| 379 String elision = null; | 377 String elision = null; |
| 380 if (count > parts.length + TAIL_COUNT) { | 378 if (count > parts.length + TAIL_COUNT) { |
| 381 elision = "..."; | 379 elision = "..."; |
| 382 length += ELLIPSIS_SIZE + OVERHEAD; | 380 length += ELLIPSIS_SIZE + OVERHEAD; |
| 383 } | 381 } |
| 384 | 382 |
| 385 // If the last two elements were very long, and we have more than | 383 // If the last two elements were very long, and we have more than |
| 386 // HEAD_COUNT elements in the initial run, drop some to make room for | 384 // HEAD_COUNT elements in the initial run, drop some to make room for |
| 387 // the last two. | 385 // the last two. |
| 388 while (length > LENGTH_LIMIT && parts.length > HEAD_COUNT) { | 386 while (length > LENGTH_LIMIT && parts.length > HEAD_COUNT) { |
| 389 length -= parts.removeLast().length + OVERHEAD; | 387 length -= parts.removeLast().length + OVERHEAD; |
| 390 if (elision == null) { | 388 if (elision == null) { |
| 391 elision = "..."; | 389 elision = "..."; |
| 392 length += ELLIPSIS_SIZE + OVERHEAD; | 390 length += ELLIPSIS_SIZE + OVERHEAD; |
| 393 } | 391 } |
| 394 } | 392 } |
| 395 if (elision != null) { | 393 if (elision != null) { |
| 396 parts.add(elision); | 394 parts.add(elision); |
| 397 } | 395 } |
| 398 parts.add(penultimateString); | 396 parts.add(penultimateString); |
| 399 parts.add(ultimateString); | 397 parts.add(ultimateString); |
| 400 } | 398 } |
| OLD | NEW |