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