Added implementation of String.codeUnits.

Based on ListIterable implementation that should be reusable for a lot
of our existing length/indexing-based iterables too.

Review URL: https://codereview.chromium.org//12261009

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@18506 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
lrn@google.com 2013-02-14 09:51:43 +00:00
parent c10172597f
commit dd31afbeca
5 changed files with 370 additions and 7 deletions

View file

@ -433,9 +433,7 @@ class _StringBase {
return result;
}
Iterable<int> get codeUnits {
throw new UnimplementedError("String.codeUnits");
}
Iterable<int> get codeUnits => new CodeUnits(this);
Runes get runes => new Runes(this);

View file

@ -4,6 +4,317 @@
part of dart._collection.dev;
/**
* An [Iterable] for classes that have efficient [length] and [elementAt].
*
* All other methods are implemented in terms of [length] and [elementAt],
* including [iterator].
*/
abstract class ListIterable<E> extends Iterable<E> {
int get length;
E elementAt(int i);
Iterator<E> get iterator => new ListIterableIterator<E>(this);
void forEach(void action(E element)) {
int length = this.length;
for (int i = 0; i < length; i++) {
action(elementAt(i));
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
}
bool get isEmpty => length != 0;
E get first {
if (length == 0) throw new StateError("No elements");
return elementAt(0);
}
E get last {
if (length == 0) throw new StateError("No elements");
return elementAt(length - 1);
}
E get single {
if (length == 0) throw new StateError("No elements");
if (length > 1) throw new StateError("Too many elements");
return elementAt(0);
}
bool contains(E element) {
int length = this.length;
for (int i = 0; i < length; i++) {
if (elementAt(i) == element) return true;
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return false;
}
bool every(bool test(E element)) {
int length = this.length;
for (int i = 0; i < length; i++) {
if (!test(elementAt(i))) return false;
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return true;
}
bool any(bool test(E element)) {
int length = this.length;
for (int i = 0; i < length; i++) {
if (test(elementAt(i))) return true;
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return false;
}
E firstMatching(bool test(E element), { E orElse() }) {
int length = this.length;
for (int i = 0; i < length; i++) {
E element = elementAt(i);
if (test(element)) return element;
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
if (orElse != null) return orElse();
throw new StateError("No matching element");
}
E lastMatching(bool test(E element), { E orElse() }) {
int length = this.length;
for (int i = length - 1; i >= 0; i++) {
E element = elementAt(i);
if (test(element)) return element;
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
if (orElse != null) return orElse();
throw new StateError("No matching element");
}
E singleMatching(bool test(E element)) {
int length = this.length;
E match = null;
bool matchFound = false;
for (int i = 0; i < length; i++) {
E element = elementAt(i);
if (test(element)) {
if (matchFound) {
throw new StateError("More than one matching element");
}
matchFound = true;
match = element;
}
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
if (matchFound) return match;
throw new StateError("No matching element");
}
E min([int compare(E a, E b)]) {
if (length == 0) return null;
if (compare == null) {
var defaultCompare = Comparable.compare;
compare = defaultCompare;
}
E min = elementAt(0);
int length = this.length;
for (int i = 1; i < length; i++) {
E element = elementAt(i);
if (compare(min, element) > 0) {
min = element;
}
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return min;
}
E max([int compare(E a, E b)]) {
if (length == 0) return null;
if (compare == null) {
var defaultCompare = Comparable.compare;
compare = defaultCompare;
}
E max = elementAt(0);
int length = this.length;
for (int i = 1; i < length; i++) {
E element = elementAt(i);
if (compare(max, element) < 0) {
max = element;
}
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return max;
}
String join([String separator]) {
int length = this.length;
if (separator != null && !separator.isEmpty) {
if (length == 0) return "";
String first = "${elementAt(0)}";
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
StringBuffer buffer = new StringBuffer(first);
for (int i = 1; i < length; i++) {
buffer.add(separator);
buffer.add("${elementAt(i)}");
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return buffer.toString();
} else {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
buffer.add("${elementAt(i)}");
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return buffer.toString();
}
}
Iterable<E> where(bool test(E element)) => super.where(test);
Iterable map(f(E element)) => new MappedIterable(this, f);
Iterable mappedBy(f(E element)) => super.mappedBy(f);
reduce(var initialValue, combine(var previousValue, E element)) {
var value = initialValue;
int length = this.length;
for (int i = 0; i < length; i++) {
value = reduce(value, elementAt(i));
if (length != this.length) {
throw new ConcurrentModificationError(this);
}
}
return value;
}
Iterable<E> skip(int count) => new SubListIterable(this, count, null);
Iterable<E> skipWhile(bool test(E element)) => super.skipWhile(test);
Iterable<E> take(int count) => new SubListIterable(this, 0, count);
Iterable<E> takeWhile(bool test(E element)) => super.takeWhile(test);
List<E> toList() {
List<E> result = new List(length);
for (int i = 0; i < length; i++) {
result[i] = elementAt(i);
}
return result;
}
Set<E> toSet() {
Set<E> result = new Set<E>();
for (int i = 0; i < length; i++) {
result.add(elementAt(i));
}
return result;
}
}
abstract class SubListIterable<E> extends ListIterable<E> {
final Iterable<E> _iterable;
final int _start;
/** If null, represents the length of the iterable. */
final int _endOrLength;
SubListIterable(this._iterable, this._start, this._endOrLength);
int get _endIndex {
int length = _iterable.length;
if (_endOrLength == null || _endOrLength > length) return length;
return _endOrLength;
}
int get _startIndex {
int length = _iterable.length;
if (_start > length) return length;
return _start;
}
int get length {
int length = _iterable.length;
if (_start >= length) return 0;
if (_endOrLength == null || _endOrLength >= length) {
return length - _start;
}
return _endOrLength - _start;
}
E elementAt(int index) {
int realIndex = _startIndex + index;
if (index < 0 || realIndex >= _endIndex) {
throw new RangeError.range(index, 0, length);
}
return _iterable.elementAt(realIndex);
}
Iterable<E> skip(int count) {
if (count < 0) throw new ArgumentError(count);
return new SubListIterable(_iterable, _start + count, _endOrLength);
}
Iterable<E> take(int count) {
if (count < 0) throw new ArgumentError(count);
if (_endOrLength == null) {
return new SubListIterable(_iterable, _start, _start + count);
} else {
newEnd = _start + count;
if (_endOrLength < newEnd) return this;
return new SubListIterable(_iterable, _start, newEnd);
}
}
}
class ListIterableIterator<E> implements Iterator<E> {
final Iterable<E> _iterable;
final int _length;
int _index;
E _current;
ListIterableIterator(Iterable<E> iterable)
: _iterable = iterable, _length = iterable.length, _index = 0;
E get current => _current;
bool moveNext() {
if (_length != _iterable.length) {
throw new ConcurrentModificationError(_iterable);
}
if (_index == _length) {
_current = null;
return false;
}
_current = _iterable.elementAt(_index);
_index++;
return true;
}
}
typedef T _Transformation<S, T>(S value);
class MappedIterable<S, T> extends Iterable<T> {

View file

@ -149,9 +149,7 @@ class JSString implements String {
return result;
}
Iterable<int> get codeUnits {
throw new UnimplementedError("String.codeUnits");
}
Iterable<int> get codeUnits => new CodeUnits(this);
Runes get runes => new Runes(this);

View file

@ -449,7 +449,6 @@ class RuneIterator implements BiDirectionalIterator<int> {
return string.substring(_position, _nextPosition);
}
bool moveNext() {
_position = _nextPosition;
if (_position == string.length) {
@ -492,3 +491,16 @@ class RuneIterator implements BiDirectionalIterator<int> {
return true;
}
}
/**
* An [Iterable] of the UTF-16 code units of a [String] in index order.
*/
class CodeUnits extends ListIterable<int> {
/** The string that this is the code units of. */
String string;
CodeUnits(this.string);
int get length => string.length;
int elementAt(int i) => string.codeUnitAt(i);
}

View file

@ -0,0 +1,44 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
main() {
test(String s) {
Iterable<int> units = s.codeUnits;
List<int> expectedUnits = <int>[];
for (int i = 0; i < s.length; i++) {
expectedUnits.add(s.codeUnitAt(i));
}
Expect.equals(s.length, units.length);
for (int i = 0; i < s.length; i++) {
Expect.equals(s.codeUnitAt(i), units.elementAt(i));
}
// for-in
var res = [];
for (int unit in units) {
res.add(unit);
}
Expect.listEquals(expectedUnits, res);
// .map
Expect.listEquals(expectedUnits.map((x) => x.toRadixString(16)).toList(),
units.map((x) => x.toRadixString(16)).toList());
}
test("abc");
test("\x00\u0000\u{000000}");
test("\u{ffff}\u{10000}\u{10ffff}");
String string = new String.fromCharCodes(
[0xdc00, 0xd800, 61, 0xd800, 0xdc00, 62, 0xdc00, 0xd800]);
test(string);
// Setting position in the middle of a surrogate pair is not allowed.
var r = new CodeUnits("\u{10000}");
var it = r.iterator;
Expect.isTrue(it.moveNext());
Expect.equals(0xD800, it.current);
Expect.isTrue(it.moveNext());
Expect.equals(0xDC00, it.current);
}