| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 part of js.wrapping; | |
| 6 | |
| 7 /// Adapter to handle a js array as a dart [List]. | |
| 8 /// You can provide a translator to automatically wrap contained Proxy to some | |
| 9 /// TypedProxy or something else. | |
| 10 class JsArrayToListAdapter<E> extends TypedProxy /*with ListMixin<E>*/ implement
s List<E> { | |
| 11 | |
| 12 /// Like [JsArrayToListAdapter.fromProxy] but with `null` handling for | |
| 13 /// [proxy]. | |
| 14 static JsArrayToListAdapter cast(Proxy proxy, [Translator translator]) => | |
| 15 proxy == null ? null : | |
| 16 new JsArrayToListAdapter.fromProxy(proxy, translator); | |
| 17 | |
| 18 /// Same as [cast] but for array containing [Serializable] elements. | |
| 19 static JsArrayToListAdapter castListOfSerializables(Proxy proxy, | |
| 20 Mapper<dynamic, Serializable> fromJs, {mapOnlyNotNull: false}) => | |
| 21 proxy == null ? null : new JsArrayToListAdapter.fromProxy(proxy, | |
| 22 new TranslatorForSerializable(fromJs, | |
| 23 mapOnlyNotNull: mapOnlyNotNull)); | |
| 24 | |
| 25 final Translator<E> _translator; | |
| 26 | |
| 27 /// Create a new adapter from a proxy of a Js list. | |
| 28 JsArrayToListAdapter.fromProxy(Proxy proxy, [Translator<E> translator]) | |
| 29 : this._translator = translator, | |
| 30 super.fromProxy(proxy); | |
| 31 | |
| 32 // Iterable | |
| 33 @override Iterator<E> get iterator => new _JsIterator<E>(this); | |
| 34 @override int get length => $unsafe.length; | |
| 35 | |
| 36 // Collection | |
| 37 @override void add(E value) { $unsafe.push(_toJs(value)); } | |
| 38 @override void clear() { this.length = 0; } | |
| 39 @override bool remove(Object element) => removeAt(indexOf(element)) != null; | |
| 40 | |
| 41 // List | |
| 42 @override E operator [](int index) { | |
| 43 if (index < 0 || index >= this.length) throw new RangeError.value(index); | |
| 44 return _fromJs($unsafe[index]); | |
| 45 } | |
| 46 @override void operator []=(int index, E value) { | |
| 47 if (index < 0 || index >= this.length) throw new RangeError.value(index); | |
| 48 $unsafe[index] = _toJs(value); | |
| 49 } | |
| 50 @override void set length(int length) { $unsafe.length = length; } | |
| 51 @override void sort([int compare(E a, E b)]) { | |
| 52 final sortedList = _asList()..sort(compare); | |
| 53 setRange(0, sortedList.length, sortedList); | |
| 54 } | |
| 55 @override void shuffle([Random random]) { | |
| 56 final shuffledList = _asList()..shuffle(random); | |
| 57 setRange(0, shuffledList.length, shuffledLength); | |
| 58 } | |
| 59 @override void insert(int index, E element) { | |
| 60 $unsafe.splice(index, 0, _toJs(element)); | |
| 61 } | |
| 62 @override E removeAt(int index) { | |
| 63 if (index < 0 || index >= this.length) throw new RangeError.value(index); | |
| 64 return _fromJs($unsafe.splice(index, 1)[0]); | |
| 65 } | |
| 66 @override E removeLast() => _fromJs($unsafe.pop()); | |
| 67 @override List<E> sublist(int start, [int end]) => | |
| 68 _asList().sublist(start, end); | |
| 69 @deprecated @override List<E> getRange(int start, int length) => | |
| 70 _asList().getRange(start, length); | |
| 71 @override void setRange(int start, int length, List<E> from, | |
| 72 [int startFrom = 0]) { | |
| 73 final args = [start, length]; | |
| 74 for(int i = startFrom; i < startFrom + length; i++) { | |
| 75 args.add(_toJs(from[i])); | |
| 76 } | |
| 77 $unsafe["splice"].apply($unsafe, array(args)); | |
| 78 } | |
| 79 @override void removeRange(int start, int end) { | |
| 80 $unsafe.splice(start, end - start); | |
| 81 } | |
| 82 | |
| 83 // private methods | |
| 84 dynamic _toJs(E e) => _translator == null ? e : _translator.toJs(e); | |
| 85 E _fromJs(dynamic value) => _translator == null ? value : | |
| 86 _translator.fromJs(value); | |
| 87 | |
| 88 List<E> _asList() { | |
| 89 final list = new List<E>(); | |
| 90 for (int i = 0; i < length; i++) { | |
| 91 list.add(this[i]); | |
| 92 } | |
| 93 return list; | |
| 94 } | |
| 95 | |
| 96 // ListMixin duplication until http://dartbug.com/9339 is fixed | |
| 97 | |
| 98 | |
| 99 // Iterable interface. | |
| 100 // Iterator<E> get iterator => new ListIterator<E>(this); | |
| 101 | |
| 102 E elementAt(int index) => this[index]; | |
| 103 | |
| 104 void forEach(void action(E element)) { | |
| 105 int length = this.length; | |
| 106 for (int i = 0; i < length; i++) { | |
| 107 action(this[i]); | |
| 108 if (length != this.length) { | |
| 109 throw new ConcurrentModificationError(this); | |
| 110 } | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 bool get isEmpty => length == 0; | |
| 115 | |
| 116 bool get isNotEmpty => !isEmpty; | |
| 117 | |
| 118 E get first { | |
| 119 if (length == 0) throw new StateError("No elements"); | |
| 120 return this[0]; | |
| 121 } | |
| 122 | |
| 123 E get last { | |
| 124 if (length == 0) throw new StateError("No elements"); | |
| 125 return this[length - 1]; | |
| 126 } | |
| 127 | |
| 128 E get single { | |
| 129 if (length == 0) throw new StateError("No elements"); | |
| 130 if (length > 1) throw new StateError("Too many elements"); | |
| 131 return this[0]; | |
| 132 } | |
| 133 | |
| 134 bool contains(E element) { | |
| 135 int length = this.length; | |
| 136 for (int i = 0; i < length; i++) { | |
| 137 if (this[i] == element) return true; | |
| 138 if (length != this.length) { | |
| 139 throw new ConcurrentModificationError(this); | |
| 140 } | |
| 141 } | |
| 142 return false; | |
| 143 } | |
| 144 | |
| 145 bool every(bool test(E element)) { | |
| 146 int length = this.length; | |
| 147 for (int i = 0; i < length; i++) { | |
| 148 if (!test(this[i])) return false; | |
| 149 if (length != this.length) { | |
| 150 throw new ConcurrentModificationError(this); | |
| 151 } | |
| 152 } | |
| 153 return true; | |
| 154 } | |
| 155 | |
| 156 bool any(bool test(E element)) { | |
| 157 int length = this.length; | |
| 158 for (int i = 0; i < length; i++) { | |
| 159 if (test(this[i])) return true; | |
| 160 if (length != this.length) { | |
| 161 throw new ConcurrentModificationError(this); | |
| 162 } | |
| 163 } | |
| 164 return false; | |
| 165 } | |
| 166 | |
| 167 E firstWhere(bool test(E element), { E orElse() }) { | |
| 168 int length = this.length; | |
| 169 for (int i = 0; i < length; i++) { | |
| 170 E element = this[i]; | |
| 171 if (test(element)) return element; | |
| 172 if (length != this.length) { | |
| 173 throw new ConcurrentModificationError(this); | |
| 174 } | |
| 175 } | |
| 176 if (orElse != null) return orElse(); | |
| 177 throw new StateError("No matching element"); | |
| 178 } | |
| 179 | |
| 180 E lastWhere(bool test(E element), { E orElse() }) { | |
| 181 int length = this.length; | |
| 182 for (int i = length - 1; i >= 0; i--) { | |
| 183 E element = this[i]; | |
| 184 if (test(element)) return element; | |
| 185 if (length != this.length) { | |
| 186 throw new ConcurrentModificationError(this); | |
| 187 } | |
| 188 } | |
| 189 if (orElse != null) return orElse(); | |
| 190 throw new StateError("No matching element"); | |
| 191 } | |
| 192 | |
| 193 E singleWhere(bool test(E element)) { | |
| 194 int length = this.length; | |
| 195 E match = null; | |
| 196 bool matchFound = false; | |
| 197 for (int i = 0; i < length; i++) { | |
| 198 E element = this[i]; | |
| 199 if (test(element)) { | |
| 200 if (matchFound) { | |
| 201 throw new StateError("More than one matching element"); | |
| 202 } | |
| 203 matchFound = true; | |
| 204 match = element; | |
| 205 } | |
| 206 if (length != this.length) { | |
| 207 throw new ConcurrentModificationError(this); | |
| 208 } | |
| 209 } | |
| 210 if (matchFound) return match; | |
| 211 throw new StateError("No matching element"); | |
| 212 } | |
| 213 | |
| 214 String join([String separator = ""]) { | |
| 215 int length = this.length; | |
| 216 if (!separator.isEmpty) { | |
| 217 if (length == 0) return ""; | |
| 218 String first = "${this[0]}"; | |
| 219 if (length != this.length) { | |
| 220 throw new ConcurrentModificationError(this); | |
| 221 } | |
| 222 StringBuffer buffer = new StringBuffer(first); | |
| 223 for (int i = 1; i < length; i++) { | |
| 224 buffer.write(separator); | |
| 225 buffer.write(this[i]); | |
| 226 if (length != this.length) { | |
| 227 throw new ConcurrentModificationError(this); | |
| 228 } | |
| 229 } | |
| 230 return buffer.toString(); | |
| 231 } else { | |
| 232 StringBuffer buffer = new StringBuffer(); | |
| 233 for (int i = 0; i < length; i++) { | |
| 234 buffer.write(this[i]); | |
| 235 if (length != this.length) { | |
| 236 throw new ConcurrentModificationError(this); | |
| 237 } | |
| 238 } | |
| 239 return buffer.toString(); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 Iterable<E> where(bool test(E element)) => _asList().where(test); | |
| 244 | |
| 245 Iterable map(f(E element)) => _asList().map(f); | |
| 246 | |
| 247 Iterable expand(Iterable f(E element)) => _asList().expand(f); | |
| 248 | |
| 249 E reduce(E combine(E previousValue, E element)) { | |
| 250 if (length == 0) throw new StateError("No elements"); | |
| 251 E value = this[0]; | |
| 252 for (int i = 1; i < length; i++) { | |
| 253 value = combine(value, this[i]); | |
| 254 } | |
| 255 return value; | |
| 256 } | |
| 257 | |
| 258 fold(var initialValue, combine(var previousValue, E element)) { | |
| 259 var value = initialValue; | |
| 260 int length = this.length; | |
| 261 for (int i = 0; i < length; i++) { | |
| 262 value = combine(value, this[i]); | |
| 263 if (length != this.length) { | |
| 264 throw new ConcurrentModificationError(this); | |
| 265 } | |
| 266 } | |
| 267 return value; | |
| 268 } | |
| 269 | |
| 270 Iterable<E> skip(int count) => _asList().skip(count); | |
| 271 | |
| 272 Iterable<E> skipWhile(bool test(E element)) => _asList().skipWhile(test); | |
| 273 | |
| 274 Iterable<E> take(int count) => _asList().take(count); | |
| 275 | |
| 276 Iterable<E> takeWhile(bool test(E element)) => _asList().takeWhile(test); | |
| 277 | |
| 278 List<E> toList({ bool growable: true }) { | |
| 279 List<E> result; | |
| 280 if (growable) { | |
| 281 result = new List<E>()..length = length; | |
| 282 } else { | |
| 283 result = new List<E>(length); | |
| 284 } | |
| 285 for (int i = 0; i < length; i++) { | |
| 286 result[i] = this[i]; | |
| 287 } | |
| 288 return result; | |
| 289 } | |
| 290 | |
| 291 Set<E> toSet() { | |
| 292 Set<E> result = new Set<E>(); | |
| 293 for (int i = 0; i < length; i++) { | |
| 294 result.add(this[i]); | |
| 295 } | |
| 296 return result; | |
| 297 } | |
| 298 | |
| 299 // Collection interface. | |
| 300 // void add(E element) { | |
| 301 // this[this.length++] = element; | |
| 302 // } | |
| 303 | |
| 304 void addAll(Iterable<E> iterable) { | |
| 305 for (E element in iterable) { | |
| 306 this[this.length++] = element; | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 // void remove(Object element) { | |
| 311 // for (int i = 0; i < this.length; i++) { | |
| 312 // if (this[i] == element) { | |
| 313 // this.setRange(i, i + this.length - 1, this, i + 1); | |
| 314 // this.length -= 1; | |
| 315 // return; | |
| 316 // } | |
| 317 // } | |
| 318 // } | |
| 319 | |
| 320 void removeWhere(bool test(E element)) { | |
| 321 _filter(this, test, false); | |
| 322 } | |
| 323 | |
| 324 void retainWhere(bool test(E element)) { | |
| 325 _filter(this, test, true); | |
| 326 } | |
| 327 | |
| 328 static void _filter(List source, | |
| 329 bool test(var element), | |
| 330 bool retainMatching) { | |
| 331 List retained = []; | |
| 332 int length = source.length; | |
| 333 for (int i = 0; i < length; i++) { | |
| 334 var element = source[i]; | |
| 335 if (test(element) == retainMatching) { | |
| 336 retained.add(element); | |
| 337 } | |
| 338 if (length != source.length) { | |
| 339 throw new ConcurrentModificationError(source); | |
| 340 } | |
| 341 } | |
| 342 if (retained.length != source.length) { | |
| 343 source.setRange(0, retained.length, retained); | |
| 344 source.length = retained.length; | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 // void clear() { this.length = 0; } | |
| 349 | |
| 350 // List interface. | |
| 351 | |
| 352 // E removeLast() { | |
| 353 // if (length == 0) { | |
| 354 // throw new StateError("No elements"); | |
| 355 // } | |
| 356 // E result = this[length - 1]; | |
| 357 // length--; | |
| 358 // return result; | |
| 359 // } | |
| 360 | |
| 361 // void sort([Comparator<E> compare]) { | |
| 362 // Sort.sort(this, compare); | |
| 363 // } | |
| 364 | |
| 365 Map<int, E> asMap() => _asList().asMap(); | |
| 366 | |
| 367 void _rangeCheck(int start, int end) { | |
| 368 if (start < 0 || start > this.length) { | |
| 369 throw new RangeError.range(start, 0, this.length); | |
| 370 } | |
| 371 if (end < start || end > this.length) { | |
| 372 throw new RangeError.range(end, start, this.length); | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 // List<E> sublist(int start, [int end]) { | |
| 377 // if (end == null) end = length; | |
| 378 // _rangeCheck(start, end); | |
| 379 // int length = end - start; | |
| 380 // List<E> result = new List<E>()..length = length; | |
| 381 // for (int i = 0; i < length; i++) { | |
| 382 // result[i] = this[start + i]; | |
| 383 // } | |
| 384 // return result; | |
| 385 // } | |
| 386 | |
| 387 // Iterable<E> getRange(int start, int end) { | |
| 388 // _rangeCheck(start, end); | |
| 389 // return new SubListIterable(this, start, end); | |
| 390 // } | |
| 391 | |
| 392 // void removeRange(int start, int end) { | |
| 393 // _rangeCheck(start, end); | |
| 394 // int length = end - start; | |
| 395 // setRange(start, this.length - length, this, end); | |
| 396 // this.length -= length; | |
| 397 // } | |
| 398 | |
| 399 void fillRange(int start, int end, [E fill]) { | |
| 400 _rangeCheck(start, end); | |
| 401 for (int i = start; i < end; i++) { | |
| 402 this[i] = fill; | |
| 403 } | |
| 404 } | |
| 405 | |
| 406 // void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0])
{ | |
| 407 // _rangeCheck(start, end); | |
| 408 // int length = end - start; | |
| 409 // if (length == 0) return; | |
| 410 // | |
| 411 // if (skipCount < 0) throw new ArgumentError(skipCount); | |
| 412 // | |
| 413 // List otherList; | |
| 414 // int otherStart; | |
| 415 // // TODO(floitsch): Make this accept more. | |
| 416 // if (iterable is List) { | |
| 417 // otherList = iterable; | |
| 418 // otherStart = skipCount; | |
| 419 // } else { | |
| 420 // otherList = iterable.skip(skipCount).toList(growable: false); | |
| 421 // otherStart = 0; | |
| 422 // } | |
| 423 // if (otherStart + length > otherList.length) { | |
| 424 // throw new StateError("Not enough elements"); | |
| 425 // } | |
| 426 // if (otherStart < start) { | |
| 427 // // Copy backwards to ensure correct copy if [from] is this. | |
| 428 // for (int i = length - 1; i >= 0; i--) { | |
| 429 // this[start + i] = otherList[otherStart + i]; | |
| 430 // } | |
| 431 // } else { | |
| 432 // for (int i = 0; i < length; i++) { | |
| 433 // this[start + i] = otherList[otherStart + i]; | |
| 434 // } | |
| 435 // } | |
| 436 // } | |
| 437 | |
| 438 void replaceRange(int start, int end, Iterable<E> newContents) { | |
| 439 // TODO(floitsch): Optimize this. | |
| 440 removeRange(start, end); | |
| 441 insertAll(start, newContents); | |
| 442 } | |
| 443 | |
| 444 int indexOf(E element, [int startIndex = 0]) { | |
| 445 if (startIndex >= this.length) { | |
| 446 return -1; | |
| 447 } | |
| 448 if (startIndex < 0) { | |
| 449 startIndex = 0; | |
| 450 } | |
| 451 for (int i = startIndex; i < this.length; i++) { | |
| 452 if (this[i] == element) { | |
| 453 return i; | |
| 454 } | |
| 455 } | |
| 456 return -1; | |
| 457 } | |
| 458 | |
| 459 /** | |
| 460 * Returns the last index in the list [a] of the given [element], starting | |
| 461 * the search at index [startIndex] to 0. | |
| 462 * Returns -1 if [element] is not found. | |
| 463 */ | |
| 464 int lastIndexOf(E element, [int startIndex]) { | |
| 465 if (startIndex == null) { | |
| 466 startIndex = this.length - 1; | |
| 467 } else { | |
| 468 if (startIndex < 0) { | |
| 469 return -1; | |
| 470 } | |
| 471 if (startIndex >= this.length) { | |
| 472 startIndex = this.length - 1; | |
| 473 } | |
| 474 } | |
| 475 for (int i = startIndex; i >= 0; i--) { | |
| 476 if (this[i] == element) { | |
| 477 return i; | |
| 478 } | |
| 479 } | |
| 480 return -1; | |
| 481 } | |
| 482 | |
| 483 // void insert(int index, E element) { | |
| 484 // if (index < 0 || index > length) { | |
| 485 // throw new RangeError.range(index, 0, length); | |
| 486 // } | |
| 487 // if (index == this.length) { | |
| 488 // add(element); | |
| 489 // return; | |
| 490 // } | |
| 491 // // We are modifying the length just below the is-check. Without the check | |
| 492 // // Array.copy could throw an exception, leaving the list in a bad state | |
| 493 // // (with a length that has been increased, but without a new element). | |
| 494 // if (index is! int) throw new ArgumentError(index); | |
| 495 // this.length++; | |
| 496 // setRange(index + 1, this.length, this, index); | |
| 497 // this[index] = element; | |
| 498 // } | |
| 499 | |
| 500 // E removeAt(int index) { | |
| 501 // E result = this[index]; | |
| 502 // setRange(index, this.length - 1, this, index + 1); | |
| 503 // length--; | |
| 504 // return result; | |
| 505 // } | |
| 506 | |
| 507 void insertAll(int index, Iterable<E> iterable) { | |
| 508 if (index < 0 || index > length) { | |
| 509 throw new RangeError.range(index, 0, length); | |
| 510 } | |
| 511 // TODO(floitsch): we can probably detect more cases. | |
| 512 if (iterable is! List && iterable is! Set /*&& iterable is! SubListIterable*
/) { | |
| 513 iterable = iterable.toList(); | |
| 514 } | |
| 515 int insertionLength = iterable.length; | |
| 516 // There might be errors after the length change, in which case the list | |
| 517 // will end up being modified but the operation not complete. Unless we | |
| 518 // always go through a "toList" we can't really avoid that. | |
| 519 this.length += insertionLength; | |
| 520 setRange(index + insertionLength, this.length, this, index); | |
| 521 setAll(index, iterable); | |
| 522 } | |
| 523 | |
| 524 void setAll(int index, Iterable<E> iterable) { | |
| 525 if (iterable is List) { | |
| 526 setRange(index, index + iterable.length, iterable); | |
| 527 } else { | |
| 528 for (E element in iterable) { | |
| 529 this[index++] = element; | |
| 530 } | |
| 531 } | |
| 532 } | |
| 533 | |
| 534 Iterable<E> get reversed => _asList().reversed; | |
| 535 | |
| 536 String toString() => _asList().toString(); | |
| 537 } | |
| 538 | |
| 539 class _JsIterator<E> implements Iterator<E> { | |
| 540 final JsArrayToListAdapter<E> _jsArray; | |
| 541 final int length; | |
| 542 int _currentIndex = -1; | |
| 543 | |
| 544 _JsIterator(JsArrayToListAdapter<E> jsArray) : this._jsArray = jsArray, | |
| 545 length = jsArray.length; | |
| 546 | |
| 547 // Iterator | |
| 548 @override bool moveNext() { | |
| 549 if (_currentIndex + 1 < length) { | |
| 550 _currentIndex++; | |
| 551 return true; | |
| 552 } | |
| 553 return false; | |
| 554 } | |
| 555 @override E get current => _jsArray[_currentIndex]; | |
| 556 } | |
| OLD | NEW |