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.core; | 5 part of dart.core; |
6 | 6 |
7 /** | 7 /** |
8 * An indexable collection of objects with a length. | 8 * An indexable collection of objects with a length. |
9 * | 9 * |
10 * Subclasses of this class implement different kinds of lists. | 10 * Subclasses of this class implement different kinds of lists. |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
116 result = <E>[]..length = length; | 116 result = <E>[]..length = length; |
117 } else { | 117 } else { |
118 result = new List<E>(length); | 118 result = new List<E>(length); |
119 } | 119 } |
120 for (int i = 0; i < length; i++) { | 120 for (int i = 0; i < length; i++) { |
121 result[i] = generator(i); | 121 result[i] = generator(i); |
122 } | 122 } |
123 return result; | 123 return result; |
124 } | 124 } |
125 | 125 |
| 126 checkMutable(reason) { |
| 127 /* TODO(jacobr): implement. |
| 128 if (this is !JSMutableArray) { |
| 129 throw new UnsupportedError(reason); |
| 130 } |
| 131 * */ |
| 132 } |
| 133 |
| 134 checkGrowable(reason) { |
| 135 /* TODO(jacobr): implement |
| 136 if (this is !JSExtendableArray) { |
| 137 throw new UnsupportedError(reason); |
| 138 } |
| 139 * */ |
| 140 } |
| 141 |
| 142 Iterable<E> where(bool f(E element)) { |
| 143 return new IterableMixinWorkaround<E>().where(this, f); |
| 144 } |
| 145 |
| 146 Iterable expand(Iterable f(E element)) { |
| 147 return IterableMixinWorkaround.expand(this, f); |
| 148 } |
| 149 |
| 150 void forEach(void f(E element)) { |
| 151 int length = this.length; |
| 152 for (int i = 0; i < length; i++) { |
| 153 f(JS('', '#[#]', this, i)); |
| 154 if (length != this.length) { |
| 155 throw new ConcurrentModificationError(this); |
| 156 } |
| 157 } |
| 158 } |
| 159 |
| 160 Iterable map(f(E element)) { |
| 161 return IterableMixinWorkaround.mapList(this, f); |
| 162 } |
| 163 |
| 164 String join([String separator = ""]) { |
| 165 var list = new List(this.length); |
| 166 for (int i = 0; i < this.length; i++) { |
| 167 list[i] = "${this[i]}"; |
| 168 } |
| 169 return JS('String', "#.join(#)", list, separator); |
| 170 } |
| 171 |
| 172 Iterable<E> take(int n) { |
| 173 return new IterableMixinWorkaround<E>().takeList(this, n); |
| 174 } |
| 175 |
| 176 Iterable<E> takeWhile(bool test(E value)) { |
| 177 return new IterableMixinWorkaround<E>().takeWhile(this, test); |
| 178 } |
| 179 |
| 180 Iterable<E> skip(int n) { |
| 181 return new IterableMixinWorkaround<E>().skipList(this, n); |
| 182 } |
| 183 |
| 184 Iterable<E> skipWhile(bool test(E value)) { |
| 185 return new IterableMixinWorkaround<E>().skipWhile(this, test); |
| 186 } |
| 187 |
| 188 E reduce(E combine(E value, E element)) { |
| 189 return IterableMixinWorkaround.reduce(this, combine); |
| 190 } |
| 191 |
| 192 fold(initialValue, combine(previousValue, E element)) { |
| 193 return IterableMixinWorkaround.fold(this, initialValue, combine); |
| 194 } |
| 195 |
| 196 E firstWhere(bool test(E value), {E orElse()}) { |
| 197 return IterableMixinWorkaround.firstWhere(this, test, orElse); |
| 198 } |
| 199 |
| 200 E lastWhere(bool test(E value), {E orElse()}) { |
| 201 return IterableMixinWorkaround.lastWhereList(this, test, orElse); |
| 202 } |
| 203 |
| 204 E singleWhere(bool test(E value)) { |
| 205 return IterableMixinWorkaround.singleWhere(this, test); |
| 206 } |
| 207 |
| 208 E elementAt(int index) { |
| 209 return this[index]; |
| 210 } |
| 211 |
| 212 E get first { |
| 213 if (length > 0) return this[0]; |
| 214 throw new StateError("No elements"); |
| 215 } |
| 216 |
| 217 E get last { |
| 218 if (length > 0) return this[length - 1]; |
| 219 throw new StateError("No elements"); |
| 220 } |
| 221 |
| 222 E get single { |
| 223 if (length == 1) return this[0]; |
| 224 if (length == 0) throw new StateError("No elements"); |
| 225 throw new StateError("More than one element"); |
| 226 } |
| 227 |
| 228 bool any(bool f(E element)) => IterableMixinWorkaround.any(this, f); |
| 229 |
| 230 bool every(bool f(E element)) => IterableMixinWorkaround.every(this, f); |
| 231 |
| 232 void sort([int compare(E a, E b)]) { |
| 233 checkMutable('sort'); |
| 234 IterableMixinWorkaround.sortList(this, compare); |
| 235 } |
| 236 |
| 237 bool contains(Object other) { |
| 238 for (int i = 0; i < length; i++) { |
| 239 if (this[i] == other) return true; |
| 240 } |
| 241 return false; |
| 242 } |
| 243 |
| 244 bool get isEmpty => length == 0; |
| 245 |
| 246 bool get isNotEmpty => !isEmpty; |
| 247 |
| 248 String toString() => ListBase.listToString(this); |
| 249 |
| 250 List<E> toList({ bool growable: true }) { |
| 251 if (growable) { |
| 252 return new JSArray<E>.markGrowable(JS('', '#.slice()', this)); |
| 253 } else { |
| 254 return new JSArray<E>.markFixed(JS('', '#.slice()', this)); |
| 255 } |
| 256 } |
| 257 |
| 258 Set<E> toSet() => new Set<E>.from(this); |
| 259 |
| 260 Iterator<E> get iterator => new ListIterator<E>(this); |
| 261 |
| 262 int get hashCode => Primitives.objectHashCode(this); |
| 263 |
| 264 // BORDER XXXX |
| 265 |
126 /** | 266 /** |
127 * Returns the object at the given [index] in the list | 267 * Returns the object at the given [index] in the list |
128 * or throws a [RangeError] if [index] is out of bounds. | 268 * or throws a [RangeError] if [index] is out of bounds. |
129 */ | 269 */ |
130 E operator [](int index); | 270 E operator [](int index) { |
| 271 if (index is !int) throw new ArgumentError(index); |
| 272 if (index >= length || index < 0) throw new RangeError.value(index); |
| 273 return JS('var', '#[#]', this, index); |
| 274 } |
131 | 275 |
132 /** | 276 /** |
133 * Sets the value at the given [index] in the list to [value] | 277 * Sets the value at the given [index] in the list to [value] |
134 * or throws a [RangeError] if [index] is out of bounds. | 278 * or throws a [RangeError] if [index] is out of bounds. |
135 */ | 279 */ |
136 void operator []=(int index, E value); | 280 void operator []=(int index, E value) { |
| 281 checkMutable('indexed set'); |
| 282 if (index is !int) throw new ArgumentError(index); |
| 283 if (index >= length || index < 0) throw new RangeError.value(index); |
| 284 JS('void', r'#[#] = #', this, index, value); |
| 285 } |
137 | 286 |
138 /** | 287 /** |
139 * Returns the number of objects in this list. | 288 * Returns the number of objects in this list. |
140 * | 289 * |
141 * The valid indices for a list are `0` through `length - 1`. | 290 * The valid indices for a list are `0` through `length - 1`. |
142 */ | 291 */ |
143 int get length; | 292 int get length => JS('JSUInt32', r'#.length', this); |
144 | 293 |
145 /** | 294 /** |
146 * Changes the length of this list. | 295 * Changes the length of this list. |
147 * | 296 * |
148 * If [newLength] is greater than | 297 * If [newLength] is greater than |
149 * the current length, entries are initialized to [:null:]. | 298 * the current length, entries are initialized to [:null:]. |
150 * | 299 * |
151 * Throws an [UnsupportedError] if the list is fixed-length. | 300 * Throws an [UnsupportedError] if the list is fixed-length. |
152 */ | 301 */ |
153 void set length(int newLength); | 302 void set length(int newLength) { |
| 303 if (newLength is !int) throw new ArgumentError(newLength); |
| 304 if (newLength < 0) throw new RangeError.value(newLength); |
| 305 checkGrowable('set length'); |
| 306 JS('void', r'#.length = #', this, newLength); |
| 307 } |
154 | 308 |
155 /** | 309 /** |
156 * Adds [value] to the end of this list, | 310 * Adds [value] to the end of this list, |
157 * extending the length by one. | 311 * extending the length by one. |
158 * | 312 * |
159 * Throws an [UnsupportedError] if the list is fixed-length. | 313 * Throws an [UnsupportedError] if the list is fixed-length. |
160 */ | 314 */ |
161 void add(E value); | 315 void add(E value) { |
| 316 checkGrowable('add'); |
| 317 JS('void', r'#.push(#)', this, value); |
| 318 } |
162 | 319 |
163 /** | 320 /** |
164 * Appends all objects of [iterable] to the end of this list. | 321 * Appends all objects of [iterable] to the end of this list. |
165 * | 322 * |
166 * Extends the length of the list by the number of objects in [iterable]. | 323 * Extends the length of the list by the number of objects in [iterable]. |
167 * Throws an [UnsupportedError] if this list is fixed-length. | 324 * Throws an [UnsupportedError] if this list is fixed-length. |
168 */ | 325 */ |
169 void addAll(Iterable<E> iterable); | 326 void addAll(Iterable<E> iterable) { |
| 327 for (E e in iterable) { |
| 328 this.add(e); |
| 329 } |
| 330 } |
170 | 331 |
171 /** | 332 /** |
172 * Returns an [Iterable] of the objects in this list in reverse order. | 333 * Returns an [Iterable] of the objects in this list in reverse order. |
173 */ | 334 */ |
174 Iterable<E> get reversed; | 335 Iterable<E> get reversed => |
| 336 new IterableMixinWorkaround<E>().reversedList(this); |
175 | 337 |
176 /** | 338 /** |
177 * Sorts this list according to the order specified by the [compare] function. | 339 * Sorts this list according to the order specified by the [compare] function. |
178 * | 340 * |
179 * The [compare] function must act as a [Comparator]. | 341 * The [compare] function must act as a [Comparator]. |
180 | 342 |
181 * List<String> numbers = ['one', 'two', 'three', 'four']; | 343 * List<String> numbers = ['one', 'two', 'three', 'four']; |
182 * // Sort from shortest to longest. | 344 * // Sort from shortest to longest. |
183 * numbers.sort((x, y) => x.length.compareTo(y.length)); | 345 * numbers.sort((x, y) => x.length.compareTo(y.length)); |
184 * numbers.join(', '); // 'one, two, four, three' | 346 * numbers.join(', '); // 'one, two, four, three' |
185 * | 347 * |
186 * The default List implementations use [Comparable.compare] if | 348 * The default List implementations use [Comparable.compare] if |
187 * [compare] is omitted. | 349 * [compare] is omitted. |
188 * | 350 * |
189 * List<int> nums = [13, 2, -11]; | 351 * List<int> nums = [13, 2, -11]; |
190 * nums.sort(); | 352 * nums.sort(); |
191 nums.join(', '); // '-11, 2, 13' | 353 nums.join(', '); // '-11, 2, 13' |
192 */ | 354 */ |
193 void sort([int compare(E a, E b)]); | 355 void sort([int compare(E a, E b)]) { |
| 356 // XXX checkMutable('sort'); |
| 357 IterableMixinWorkaround.sortList(this, compare); |
| 358 } |
194 | 359 |
195 /** | 360 /** |
196 * Shuffles the elements of this list randomly. | 361 * Shuffles the elements of this list randomly. |
197 */ | 362 */ |
198 void shuffle([Random random]); | 363 void shuffle([Random random]) { |
| 364 IterableMixinWorkaround.shuffleList(this, random); |
| 365 } |
199 | 366 |
200 /** | 367 /** |
201 * Returns the first index of [element] in this list. | 368 * Returns the first index of [element] in this list. |
202 * | 369 * |
203 * Searches the list from index [start] to the end of the list. | 370 * Searches the list from index [start] to the end of the list. |
204 * The first time an object [:o:] is encountered so that [:o == element:], | 371 * The first time an object [:o:] is encountered so that [:o == element:], |
205 * the index of [:o:] is returned. | 372 * the index of [:o:] is returned. |
206 * | 373 * |
207 * List<String> notes = ['do', 're', 'mi', 're']; | 374 * List<String> notes = ['do', 're', 'mi', 're']; |
208 * notes.indexOf('re'); // 1 | 375 * notes.indexOf('re'); // 1 |
209 * notes.indexOf('re', 2); // 3 | 376 * notes.indexOf('re', 2); // 3 |
210 * | 377 * |
211 * Returns -1 if [element] is not found. | 378 * Returns -1 if [element] is not found. |
212 * | 379 * |
213 * notes.indexOf('fa'); // -1 | 380 * notes.indexOf('fa'); // -1 |
214 */ | 381 */ |
215 int indexOf(E element, [int start = 0]); | 382 int indexOf(E element, [int start = 0]) { |
| 383 return IterableMixinWorkaround.indexOfList(this, element, start); |
| 384 } |
216 | 385 |
217 /** | 386 /** |
218 * Returns the last index of [element] in this list. | 387 * Returns the last index of [element] in this list. |
219 * | 388 * |
220 * Searches the list backwards from index [start] to 0. | 389 * Searches the list backwards from index [start] to 0. |
221 * | 390 * |
222 * The first time an object [:o:] is encountered so that [:o == element:], | 391 * The first time an object [:o:] is encountered so that [:o == element:], |
223 * the index of [:o:] is returned. | 392 * the index of [:o:] is returned. |
224 * | 393 * |
225 * List<String> notes = ['do', 're', 'mi', 're']; | 394 * List<String> notes = ['do', 're', 'mi', 're']; |
226 * notes.lastIndexOf('re', 2); // 1 | 395 * notes.lastIndexOf('re', 2); // 1 |
227 * | 396 * |
228 * If [start] is not provided, this method searches from the end of the | 397 * If [start] is not provided, this method searches from the end of the |
229 * list./Returns | 398 * list./Returns |
230 * | 399 * |
231 * notes.lastIndexOf('re'); // 3 | 400 * notes.lastIndexOf('re'); // 3 |
232 * | 401 * |
233 * Returns -1 if [element] is not found. | 402 * Returns -1 if [element] is not found. |
234 * | 403 * |
235 * notes.lastIndexOf('fa'); // -1 | 404 * notes.lastIndexOf('fa'); // -1 |
236 */ | 405 */ |
237 int lastIndexOf(E element, [int start]); | 406 int lastIndexOf(E element, [int start]) { |
| 407 return IterableMixinWorkaround.lastIndexOfList(this, element, start); |
| 408 } |
238 | 409 |
239 /** | 410 /** |
240 * Removes all objects from this list; | 411 * Removes all objects from this list; |
241 * the length of the list becomes zero. | 412 * the length of the list becomes zero. |
242 * | 413 * |
243 * Throws an [UnsupportedError], and retains all objects, if this | 414 * Throws an [UnsupportedError], and retains all objects, if this |
244 * is a fixed-length list. | 415 * is a fixed-length list. |
245 */ | 416 */ |
246 void clear(); | 417 void clear() { |
| 418 length = 0; |
| 419 } |
247 | 420 |
248 /** | 421 /** |
249 * Inserts the object at position [index] in this list. | 422 * Inserts the object at position [index] in this list. |
250 * | 423 * |
251 * This increases the length of the list by one and shifts all objects | 424 * This increases the length of the list by one and shifts all objects |
252 * at or after the index towards the end of the list. | 425 * at or after the index towards the end of the list. |
253 * | 426 * |
254 * An error occurs if the [index] is less than 0 or greater than length. | 427 * An error occurs if the [index] is less than 0 or greater than length. |
255 * An [UnsupportedError] occurs if the list is fixed-length. | 428 * An [UnsupportedError] occurs if the list is fixed-length. |
256 */ | 429 */ |
257 void insert(int index, E element); | 430 void insert(int index, E element) { |
| 431 if (index is !int) throw new ArgumentError(index); |
| 432 if (index < 0 || index > length) { |
| 433 throw new RangeError.value(index); |
| 434 } |
| 435 checkGrowable('insert'); |
| 436 JS('void', r'#.splice(#, 0, #)', this, index, element); |
| 437 } |
| 438 |
258 | 439 |
259 /** | 440 /** |
260 * Inserts all objects of [iterable] at position [index] in this list. | 441 * Inserts all objects of [iterable] at position [index] in this list. |
261 * | 442 * |
262 * This increases the length of the list by the length of [iterable] and | 443 * This increases the length of the list by the length of [iterable] and |
263 * shifts all later objects towards the end of the list. | 444 * shifts all later objects towards the end of the list. |
264 * | 445 * |
265 * An error occurs if the [index] is less than 0 or greater than length. | 446 * An error occurs if the [index] is less than 0 or greater than length. |
266 * An [UnsupportedError] occurs if the list is fixed-length. | 447 * An [UnsupportedError] occurs if the list is fixed-length. |
267 */ | 448 */ |
268 void insertAll(int index, Iterable<E> iterable); | 449 void insertAll(int index, Iterable<E> iterable) { |
| 450 checkGrowable('insertAll'); |
| 451 IterableMixinWorkaround.insertAllList(this, index, iterable); |
| 452 } |
269 | 453 |
270 /** | 454 /** |
271 * Overwrites objects of `this` with the objects of [iterable], starting | 455 * Overwrites objects of `this` with the objects of [iterable], starting |
272 * at position [index] in this list. | 456 * at position [index] in this list. |
273 * | 457 * |
274 * List<String> list = ['a', 'b', 'c']; | 458 * List<String> list = ['a', 'b', 'c']; |
275 * list.setAll(1, ['bee', 'sea']); | 459 * list.setAll(1, ['bee', 'sea']); |
276 * list.join(', '); // 'a, bee, sea' | 460 * list.join(', '); // 'a, bee, sea' |
277 * | 461 * |
278 * This operation does not increase the length of `this`. | 462 * This operation does not increase the length of `this`. |
279 * | 463 * |
280 * The [index] must be non-negative and no greater than [length]. | 464 * The [index] must be non-negative and no greater than [length]. |
281 * | 465 * |
282 * The [iterable] must not have more elements than what can fit from [index] | 466 * The [iterable] must not have more elements than what can fit from [index] |
283 * to [length]. | 467 * to [length]. |
284 * | 468 * |
285 * If `iterable` is based on this list, its values may change /during/ the | 469 * If `iterable` is based on this list, its values may change /during/ the |
286 * `setAll` operation. | 470 * `setAll` operation. |
287 */ | 471 */ |
288 void setAll(int index, Iterable<E> iterable); | 472 void setAll(int index, Iterable<E> iterable) { |
| 473 checkMutable('setAll'); |
| 474 IterableMixinWorkaround.setAllList(this, index, iterable); |
| 475 } |
289 | 476 |
290 /** | 477 /** |
291 * Removes the first occurence of [value] from this list. | 478 * Removes the first occurence of [value] from this list. |
292 * | 479 * |
293 * Returns true if [value] was in the list, false otherwise. | 480 * Returns true if [value] was in the list, false otherwise. |
294 * | 481 * |
295 * List<String> parts = ['head', 'shoulders', 'knees', 'toes']; | 482 * List<String> parts = ['head', 'shoulders', 'knees', 'toes']; |
296 * parts.remove('head'); // true | 483 * parts.remove('head'); // true |
297 * parts.join(', '); // 'shoulders, knees, toes' | 484 * parts.join(', '); // 'shoulders, knees, toes' |
298 * | 485 * |
299 * The method has no effect if [value] was not in the list. | 486 * The method has no effect if [value] was not in the list. |
300 * | 487 * |
301 * // Note: 'head' has already been removed. | 488 * // Note: 'head' has already been removed. |
302 * parts.remove('head'); // false | 489 * parts.remove('head'); // false |
303 * parts.join(', '); // 'shoulders, knees, toes' | 490 * parts.join(', '); // 'shoulders, knees, toes' |
304 * | 491 * |
305 * An [UnsupportedError] occurs if the list is fixed-length. | 492 * An [UnsupportedError] occurs if the list is fixed-length. |
306 */ | 493 */ |
307 bool remove(Object value); | 494 bool remove(Object element) { |
| 495 checkGrowable('remove'); |
| 496 for (int i = 0; i < this.length; i++) { |
| 497 if (this[i] == value) { |
| 498 JS('var', r'#.splice(#, 1)', this, i); |
| 499 return true; |
| 500 } |
| 501 } |
| 502 return false; |
| 503 } |
308 | 504 |
309 /** | 505 /** |
310 * Removes the object at position [index] from this list. | 506 * Removes the object at position [index] from this list. |
311 * | 507 * |
312 * This method reduces the length of `this` by one and moves all later objects | 508 * This method reduces the length of `this` by one and moves all later objects |
313 * down by one position. | 509 * down by one position. |
314 * | 510 * |
315 * Returns the removed object. | 511 * Returns the removed object. |
316 * | 512 * |
317 * The [index] must be in the range `0 ≤ index < length`. | 513 * The [index] must be in the range `0 ≤ index < length`. |
318 * | 514 * |
319 * Throws an [UnsupportedError] if this is a fixed-length list. In that case | 515 * Throws an [UnsupportedError] if this is a fixed-length list. In that case |
320 * the list is not modified. | 516 * the list is not modified. |
321 */ | 517 */ |
322 E removeAt(int index); | 518 E removeAt(int index) { |
| 519 if (index is !int) throw new ArgumentError(index); |
| 520 if (index < 0 || index >= length) { |
| 521 throw new RangeError.value(index); |
| 522 } |
| 523 checkGrowable('removeAt'); |
| 524 return JS('var', r'#.splice(#, 1)[0]', this, index); |
| 525 } |
323 | 526 |
324 /** | 527 /** |
325 * Pops and returns the last object in this list. | 528 * Pops and returns the last object in this list. |
326 * | 529 * |
327 * Throws an [UnsupportedError] if this is a fixed-length list. | 530 * Throws an [UnsupportedError] if this is a fixed-length list. |
328 */ | 531 */ |
329 E removeLast(); | 532 E removeLast() { |
| 533 checkGrowable('removeLast'); |
| 534 if (length == 0) throw new RangeError.value(-1); |
| 535 return JS('var', r'#.pop()', this); |
| 536 } |
330 | 537 |
331 /** | 538 /** |
332 * Removes all objects from this list that satisfy [test]. | 539 * Removes all objects from this list that satisfy [test]. |
333 * | 540 * |
334 * An object [:o:] satisfies [test] if [:test(o):] is true. | 541 * An object [:o:] satisfies [test] if [:test(o):] is true. |
335 * | 542 * |
336 * List<String> numbers = ['one', 'two', 'three', 'four']; | 543 * List<String> numbers = ['one', 'two', 'three', 'four']; |
337 * numbers.removeWhere((item) => item.length == 3); | 544 * numbers.removeWhere((item) => item.length == 3); |
338 * numbers.join(', '); // 'three, four' | 545 * numbers.join(', '); // 'three, four' |
339 * | 546 * |
340 * Throws an [UnsupportedError] if this is a fixed-length list. | 547 * Throws an [UnsupportedError] if this is a fixed-length list. |
341 */ | 548 */ |
342 void removeWhere(bool test(E element)); | 549 void removeWhere(bool test(E element)) { |
| 550 // This could, and should, be optimized. |
| 551 IterableMixinWorkaround.removeWhereList(this, test); |
| 552 } |
| 553 |
343 | 554 |
344 /** | 555 /** |
345 * Removes all objects from this list that fail to satisfy [test]. | 556 * Removes all objects from this list that fail to satisfy [test]. |
346 * | 557 * |
347 * An object [:o:] satisfies [test] if [:test(o):] is true. | 558 * An object [:o:] satisfies [test] if [:test(o):] is true. |
348 * | 559 * |
349 * List<String> numbers = ['one', 'two', 'three', 'four']; | 560 * List<String> numbers = ['one', 'two', 'three', 'four']; |
350 * numbers.retainWhere((item) => item.length == 3); | 561 * numbers.retainWhere((item) => item.length == 3); |
351 * numbers.join(', '); // 'one, two' | 562 * numbers.join(', '); // 'one, two' |
352 * | 563 * |
353 * Throws an [UnsupportedError] if this is a fixed-length list. | 564 * Throws an [UnsupportedError] if this is a fixed-length list. |
354 */ | 565 */ |
355 void retainWhere(bool test(E element)); | 566 void retainWhere(bool test(E element)) { |
| 567 IterableMixinWorkaround.removeWhereList(this, |
| 568 (E element) => !test(element)); |
| 569 } |
356 | 570 |
357 /** | 571 /** |
358 * Returns a new list containing the objects from [start] inclusive to [end] | 572 * Returns a new list containing the objects from [start] inclusive to [end] |
359 * exclusive. | 573 * exclusive. |
360 * | 574 * |
361 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; | 575 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; |
362 * colors.sublist(1, 3); // ['green', 'blue'] | 576 * colors.sublist(1, 3); // ['green', 'blue'] |
363 * | 577 * |
364 * If [end] is omitted, the [length] of `this` is used. | 578 * If [end] is omitted, the [length] of `this` is used. |
365 * | 579 * |
366 * colors.sublist(1); // ['green', 'blue', 'orange', 'pink'] | 580 * colors.sublist(1); // ['green', 'blue', 'orange', 'pink'] |
367 * | 581 * |
368 * An error occurs if [start] is outside the range `0` .. `length` or if | 582 * An error occurs if [start] is outside the range `0` .. `length` or if |
369 * [end] is outside the range `start` .. `length`. | 583 * [end] is outside the range `start` .. `length`. |
370 */ | 584 */ |
371 List<E> sublist(int start, [int end]); | 585 List<E> sublist(int start, [int end]) { |
| 586 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. |
| 587 if (start is !int) throw new ArgumentError(start); |
| 588 if (start < 0 || start > length) { |
| 589 throw new RangeError.range(start, 0, length); |
| 590 } |
| 591 if (end == null) { |
| 592 end = length; |
| 593 } else { |
| 594 if (end is !int) throw new ArgumentError(end); |
| 595 if (end < start || end > length) { |
| 596 throw new RangeError.range(end, start, length); |
| 597 } |
| 598 } |
| 599 if (start == end) return <E>[]; |
| 600 return new JSArray<E>.markGrowable( |
| 601 JS('', r'#.slice(#, #)', this, start, end)); |
| 602 } |
372 | 603 |
373 /** | 604 /** |
374 * Returns an [Iterable] that iterates over the objects in the range | 605 * Returns an [Iterable] that iterates over the objects in the range |
375 * [start] inclusive to [end] exclusive. | 606 * [start] inclusive to [end] exclusive. |
376 * | 607 * |
377 * An error occurs if [end] is before [start]. | 608 * An error occurs if [end] is before [start]. |
378 * | 609 * |
379 * An error occurs if the [start] and [end] are not valid ranges at the time | 610 * An error occurs if the [start] and [end] are not valid ranges at the time |
380 * of the call to this method. The returned [Iterable] behaves like | 611 * of the call to this method. The returned [Iterable] behaves like |
381 * `skip(start).take(end - start)`. That is, it does not throw exceptions | 612 * `skip(start).take(end - start)`. That is, it does not throw exceptions |
382 * if `this` changes size. | 613 * if `this` changes size. |
383 * | 614 * |
384 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; | 615 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; |
385 * Iterable<String> range = colors.getRange(1, 4); | 616 * Iterable<String> range = colors.getRange(1, 4); |
386 * range.join(', '); // 'green, blue, orange' | 617 * range.join(', '); // 'green, blue, orange' |
387 * colors.length = 3; | 618 * colors.length = 3; |
388 * range.join(', '); // 'green, blue' | 619 * range.join(', '); // 'green, blue' |
389 */ | 620 */ |
390 Iterable<E> getRange(int start, int end); | 621 Iterable<E> getRange(int start, int end) { |
| 622 return new IterableMixinWorkaround<E>().getRangeList(this, start, end); |
| 623 } |
| 624 |
391 | 625 |
392 /** | 626 /** |
393 * Copies the objects of [iterable], skipping [skipCount] objects first, | 627 * Copies the objects of [iterable], skipping [skipCount] objects first, |
394 * into the range [start], inclusive, to [end], exclusive, of the list. | 628 * into the range [start], inclusive, to [end], exclusive, of the list. |
395 * | 629 * |
396 * List<int> list1 = [1, 2, 3, 4]; | 630 * List<int> list1 = [1, 2, 3, 4]; |
397 * List<int> list2 = [5, 6, 7, 8, 9]; | 631 * List<int> list2 = [5, 6, 7, 8, 9]; |
398 * // Copies the 4th and 5th items in list2 as the 2nd and 3rd items | 632 * // Copies the 4th and 5th items in list2 as the 2nd and 3rd items |
399 * // of list1. | 633 * // of list1. |
400 * list1.setRange(1, 3, list2, 3); | 634 * list1.setRange(1, 3, list2, 3); |
401 * list1.join(', '); // '1, 8, 9, 4' | 635 * list1.join(', '); // '1, 8, 9, 4' |
402 * | 636 * |
403 * The [start] and [end] indices must satisfy `0 ≤ start ≤ end ≤ length`. | 637 * The [start] and [end] indices must satisfy `0 ≤ start ≤ end ≤ length`. |
404 * If [start] equals [end], this method has no effect. | 638 * If [start] equals [end], this method has no effect. |
405 * | 639 * |
406 * The [iterable] must have enough objects to fill the range from `start` | 640 * The [iterable] must have enough objects to fill the range from `start` |
407 * to `end` after skipping [skipCount] objects. | 641 * to `end` after skipping [skipCount] objects. |
408 * | 642 * |
409 * If `iterable` is this list, the operation will copy the elements originally | 643 * If `iterable` is this list, the operation will copy the elements originally |
410 * in the range from `skipCount` to `skipCount + (end - start)` to the | 644 * in the range from `skipCount` to `skipCount + (end - start)` to the |
411 * range `start` to `end`, even if the two ranges overlap. | 645 * range `start` to `end`, even if the two ranges overlap. |
412 * | 646 * |
413 * If `iterable` depends on this list in some other way, no guarantees are | 647 * If `iterable` depends on this list in some other way, no guarantees are |
414 * made. | 648 * made. |
415 */ | 649 */ |
416 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]); | 650 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { |
| 651 checkMutable('set range'); |
| 652 IterableMixinWorkaround.setRangeList(this, start, end, iterable, skipCount); |
| 653 } |
417 | 654 |
418 /** | 655 /** |
419 * Removes the objects in the range [start] inclusive to [end] exclusive. | 656 * Removes the objects in the range [start] inclusive to [end] exclusive. |
420 * | 657 * |
421 * The [start] and [end] indices must be in the range | 658 * The [start] and [end] indices must be in the range |
422 * `0 ≤ index ≤ length`, and `start ≤ end`. | 659 * `0 ≤ index ≤ length`, and `start ≤ end`. |
423 * | 660 * |
424 * Throws an [UnsupportedError] if this is a fixed-length list. In that case | 661 * Throws an [UnsupportedError] if this is a fixed-length list. In that case |
425 * the list is not modified. | 662 * the list is not modified. |
426 */ | 663 */ |
427 void removeRange(int start, int end); | 664 void removeRange(int start, int end) { |
| 665 checkGrowable('removeRange'); |
| 666 int receiverLength = this.length; |
| 667 if (start < 0 || start > receiverLength) { |
| 668 throw new RangeError.range(start, 0, receiverLength); |
| 669 } |
| 670 if (end < start || end > receiverLength) { |
| 671 throw new RangeError.range(end, start, receiverLength); |
| 672 } |
| 673 Lists.copy(this, |
| 674 end, |
| 675 this, |
| 676 start, |
| 677 receiverLength - end); |
| 678 this.length = receiverLength - (end - start); |
| 679 } |
428 | 680 |
429 /** | 681 /** |
430 * Sets the objects in the range [start] inclusive to [end] exclusive | 682 * Sets the objects in the range [start] inclusive to [end] exclusive |
431 * to the given [fillValue]. | 683 * to the given [fillValue]. |
432 * | 684 * |
433 * An error occurs if [start]..[end] is not a valid range for `this`. | 685 * An error occurs if [start]..[end] is not a valid range for `this`. |
434 */ | 686 */ |
435 void fillRange(int start, int end, [E fillValue]); | 687 void fillRange(int start, int end, [E fillValue]) { |
| 688 checkMutable('fill range'); |
| 689 IterableMixinWorkaround.fillRangeList(this, start, end, fillValue); |
| 690 } |
436 | 691 |
437 /** | 692 /** |
438 * Removes the objects in the range [start] inclusive to [end] exclusive | 693 * Removes the objects in the range [start] inclusive to [end] exclusive |
439 * and inserts the contents of [replacement] in its place. | 694 * and inserts the contents of [replacement] in its place. |
440 * | 695 * |
441 * List<int> list = [1, 2, 3, 4, 5]; | 696 * List<int> list = [1, 2, 3, 4, 5]; |
442 * list.replaceRange(1, 4, [6, 7]); | 697 * list.replaceRange(1, 4, [6, 7]); |
443 * list.join(', '); // '1, 6, 7, 5' | 698 * list.join(', '); // '1, 6, 7, 5' |
444 * | 699 * |
445 * An error occurs if [start]..[end] is not a valid range for `this`. | 700 * An error occurs if [start]..[end] is not a valid range for `this`. |
446 */ | 701 */ |
447 void replaceRange(int start, int end, Iterable<E> replacement); | 702 void replaceRange(int start, int end, Iterable<E> replacement) { |
| 703 checkGrowable('removeRange'); |
| 704 IterableMixinWorkaround.replaceRangeList(this, start, end, replacement); |
| 705 } |
448 | 706 |
449 /** | 707 /** |
450 * Returns an unmodifiable [Map] view of `this`. | 708 * Returns an unmodifiable [Map] view of `this`. |
451 * | 709 * |
452 * The map uses the indices of this list as keys and the corresponding objects | 710 * The map uses the indices of this list as keys and the corresponding objects |
453 * as values. The `Map.keys` [Iterable] iterates the indices of this list | 711 * as values. The `Map.keys` [Iterable] iterates the indices of this list |
454 * in numerical order. | 712 * in numerical order. |
455 * | 713 * |
456 * List<String> words = ['fee', 'fi', 'fo', 'fum']; | 714 * List<String> words = ['fee', 'fi', 'fo', 'fum']; |
457 * Map<int, String> map = words.asMap(); | 715 * Map<int, String> map = words.asMap(); |
458 * map[0] + map[1]; // 'feefi'; | 716 * map[0] + map[1]; // 'feefi'; |
459 * map.keys.toList(); // [0, 1, 2, 3] | 717 * map.keys.toList(); // [0, 1, 2, 3] |
460 */ | 718 */ |
461 Map<int, E> asMap(); | 719 Map<int, E> asMap() { |
| 720 return new IterableMixinWorkaround<E>().asMapList(this); |
| 721 } |
462 } | 722 } |
OLD | NEW |