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