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 |