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 const int _maxAscii = 0x7f; | 5 const int _maxAscii = 0x7f; |
6 const int _maxLatin1 = 0xff; | 6 const int _maxLatin1 = 0xff; |
7 const int _maxUtf16 = 0xffff; | 7 const int _maxUtf16 = 0xffff; |
8 const int _maxUnicode = 0x10ffff; | 8 const int _maxUnicode = 0x10ffff; |
9 | 9 |
10 @patch | 10 @patch class String { |
11 class String { | 11 @patch factory String.fromCharCodes(Iterable<int> charCodes, |
12 @patch | 12 [int start = 0, int end]) { |
13 factory String.fromCharCodes(Iterable<int> charCodes, | 13 if (charCodes is! Iterable) throw new ArgumentError.value(charCodes, "charCo
des"); |
14 [int start = 0, int end]) { | |
15 if (charCodes is! Iterable) | |
16 throw new ArgumentError.value(charCodes, "charCodes"); | |
17 if (start is! int) throw new ArgumentError.value(start, "start"); | 14 if (start is! int) throw new ArgumentError.value(start, "start"); |
18 if (end != null && end is! int) throw new ArgumentError.value(end, "end"); | 15 if (end != null && end is! int) throw new ArgumentError.value(end, "end"); |
19 return _StringBase.createFromCharCodes(charCodes, start, end, null); | 16 return _StringBase.createFromCharCodes(charCodes, start, end, null); |
20 } | 17 } |
21 | 18 |
22 @patch | 19 @patch factory String.fromCharCode(int charCode) { |
23 factory String.fromCharCode(int charCode) { | |
24 if (charCode >= 0) { | 20 if (charCode >= 0) { |
25 if (charCode <= 0xff) { | 21 if (charCode <= 0xff) { |
26 return _OneByteString._allocate(1).._setAt(0, charCode); | 22 return _OneByteString._allocate(1).._setAt(0, charCode); |
27 } | 23 } |
28 if (charCode <= 0xffff) { | 24 if (charCode <= 0xffff) { |
29 return _StringBase._createFromCodePoints( | 25 return _StringBase._createFromCodePoints(new _List(1)..[0] = charCode, |
30 new _List(1)..[0] = charCode, 0, 1); | 26 0, 1); |
31 } | 27 } |
32 if (charCode <= 0x10ffff) { | 28 if (charCode <= 0x10ffff) { |
33 var low = 0xDC00 | (charCode & 0x3ff); | 29 var low = 0xDC00 | (charCode & 0x3ff); |
34 int bits = charCode - 0x10000; | 30 int bits = charCode - 0x10000; |
35 var high = 0xD800 | (bits >> 10); | 31 var high = 0xD800 | (bits >> 10); |
36 return _StringBase._createFromCodePoints( | 32 return _StringBase._createFromCodePoints(new _List(2)..[0] = high |
37 new _List(2) | 33 ..[1] = low, |
38 ..[0] = high | 34 0, 2); |
39 ..[1] = low, | |
40 0, | |
41 2); | |
42 } | 35 } |
43 } | 36 } |
44 throw new RangeError.range(charCode, 0, 0x10ffff); | 37 throw new RangeError.range(charCode, 0, 0x10ffff); |
45 } | 38 } |
46 | 39 |
47 @patch | 40 @patch const factory String.fromEnvironment(String name, |
48 const factory String.fromEnvironment(String name, {String defaultValue}) | 41 {String defaultValue}) |
49 native "String_fromEnvironment"; | 42 native "String_fromEnvironment"; |
50 } | 43 } |
51 | 44 |
| 45 |
52 /** | 46 /** |
53 * [_StringBase] contains common methods used by concrete String | 47 * [_StringBase] contains common methods used by concrete String |
54 * implementations, e.g., _OneByteString. | 48 * implementations, e.g., _OneByteString. |
55 */ | 49 */ |
56 abstract class _StringBase { | 50 abstract class _StringBase { |
57 // Constants used by replaceAll encoding of string slices between matches. | 51 // Constants used by replaceAll encoding of string slices between matches. |
58 // A string slice (start+length) is encoded in a single Smi to save memory | 52 // A string slice (start+length) is encoded in a single Smi to save memory |
59 // overhead in the common case. | 53 // overhead in the common case. |
60 // We use fewer bits for length (11 bits) than for the start index (19+ bits). | 54 // We use fewer bits for length (11 bits) than for the start index (19+ bits). |
61 // For long strings, it's possible to have many large indices, | 55 // For long strings, it's possible to have many large indices, |
(...skipping 16 matching lines...) Expand all Loading... |
78 // We pick 30 as a safe lower bound on available bits in a negative smi. | 72 // We pick 30 as a safe lower bound on available bits in a negative smi. |
79 // TODO(lrn): Consider allowing more bits for start on 64-bit systems. | 73 // TODO(lrn): Consider allowing more bits for start on 64-bit systems. |
80 static const int _maxUnsignedSmiBits = 30; | 74 static const int _maxUnsignedSmiBits = 30; |
81 | 75 |
82 // For longer strings, calling into C++ to create the result of a | 76 // For longer strings, calling into C++ to create the result of a |
83 // [replaceAll] is faster than [_joinReplaceAllOneByteResult]. | 77 // [replaceAll] is faster than [_joinReplaceAllOneByteResult]. |
84 // TODO(lrn): See if this limit can be tweaked. | 78 // TODO(lrn): See if this limit can be tweaked. |
85 static const int _maxJoinReplaceOneByteStringLength = 500; | 79 static const int _maxJoinReplaceOneByteStringLength = 500; |
86 | 80 |
87 factory _StringBase._uninstantiable() { | 81 factory _StringBase._uninstantiable() { |
88 throw new UnsupportedError("_StringBase can't be instaniated"); | 82 throw new UnsupportedError( |
| 83 "_StringBase can't be instaniated"); |
89 } | 84 } |
90 | 85 |
91 int get hashCode native "String_getHashCode"; | 86 int get hashCode native "String_getHashCode"; |
92 | 87 |
93 bool get _isOneByte { | 88 bool get _isOneByte { |
94 // Alternatively return false and override it on one-byte string classes. | 89 // Alternatively return false and override it on one-byte string classes. |
95 int id = ClassID.getID(this); | 90 int id = ClassID.getID(this); |
96 return id == ClassID.cidOneByteString || | 91 return id == ClassID.cidOneByteString || |
97 id == ClassID.cidExternalOneByteString; | 92 id == ClassID.cidExternalOneByteString; |
98 } | 93 } |
99 | 94 |
100 /** | 95 /** |
101 * Create the most efficient string representation for specified | 96 * Create the most efficient string representation for specified |
102 * [charCodes]. | 97 * [charCodes]. |
103 * | 98 * |
104 * Only uses the character codes betwen index [start] and index [end] of | 99 * Only uses the character codes betwen index [start] and index [end] of |
105 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. | 100 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. |
106 * | 101 * |
107 * The [limit] is an upper limit on the character codes in the iterable. | 102 * The [limit] is an upper limit on the character codes in the iterable. |
108 * It's `null` if unknown. | 103 * It's `null` if unknown. |
109 */ | 104 */ |
110 static String createFromCharCodes( | 105 static String createFromCharCodes(Iterable<int> charCodes, |
111 Iterable<int> charCodes, int start, int end, int limit) { | 106 int start, int end, |
| 107 int limit) { |
112 if (start == null) throw new ArgumentError.notNull("start"); | 108 if (start == null) throw new ArgumentError.notNull("start"); |
113 if (charCodes == null) throw new ArgumentError(charCodes); | 109 if (charCodes == null) throw new ArgumentError(charCodes); |
114 // TODO(srdjan): Also skip copying of wide typed arrays. | 110 // TODO(srdjan): Also skip copying of wide typed arrays. |
115 final ccid = ClassID.getID(charCodes); | 111 final ccid = ClassID.getID(charCodes); |
116 bool isOneByteString = false; | 112 bool isOneByteString = false; |
117 if ((ccid != ClassID.cidArray) && | 113 if ((ccid != ClassID.cidArray) && |
118 (ccid != ClassID.cidGrowableObjectArray) && | 114 (ccid != ClassID.cidGrowableObjectArray) && |
119 (ccid != ClassID.cidImmutableArray)) { | 115 (ccid != ClassID.cidImmutableArray)) { |
120 if (charCodes is Uint8List) { | 116 if (charCodes is Uint8List) { |
121 end = RangeError.checkValidRange(start, end, charCodes.length); | 117 end = RangeError.checkValidRange(start, end, charCodes.length); |
(...skipping 27 matching lines...) Expand all Loading... |
149 static int _scanCodeUnits(List<int> charCodes, int start, int end) { | 145 static int _scanCodeUnits(List<int> charCodes, int start, int end) { |
150 int bits = 0; | 146 int bits = 0; |
151 for (int i = start; i < end; i++) { | 147 for (int i = start; i < end; i++) { |
152 int code = charCodes[i]; | 148 int code = charCodes[i]; |
153 if (code is! _Smi) throw new ArgumentError(charCodes); | 149 if (code is! _Smi) throw new ArgumentError(charCodes); |
154 bits |= code; | 150 bits |= code; |
155 } | 151 } |
156 return bits; | 152 return bits; |
157 } | 153 } |
158 | 154 |
159 static String _createStringFromIterable( | 155 static String _createStringFromIterable(Iterable<int> charCodes, |
160 Iterable<int> charCodes, int start, int end) { | 156 int start, int end) { |
161 // Treat charCodes as Iterable. | 157 // Treat charCodes as Iterable. |
162 if (charCodes is EfficientLengthIterable) { | 158 if (charCodes is EfficientLengthIterable) { |
163 int length = charCodes.length; | 159 int length = charCodes.length; |
164 end = RangeError.checkValidRange(start, end, length); | 160 end = RangeError.checkValidRange(start, end, length); |
165 List charCodeList = | 161 List charCodeList = new List.from(charCodes.take(end).skip(start), |
166 new List.from(charCodes.take(end).skip(start), growable: false); | 162 growable: false); |
167 return createFromCharCodes(charCodeList, 0, charCodeList.length, null); | 163 return createFromCharCodes(charCodeList, 0, charCodeList.length, null); |
168 } | 164 } |
169 // Don't know length of iterable, so iterate and see if all the values | 165 // Don't know length of iterable, so iterate and see if all the values |
170 // are there. | 166 // are there. |
171 if (start < 0) throw new RangeError.range(start, 0, charCodes.length); | 167 if (start < 0) throw new RangeError.range(start, 0, charCodes.length); |
172 var it = charCodes.iterator; | 168 var it = charCodes.iterator; |
173 for (int i = 0; i < start; i++) { | 169 for (int i = 0; i < start; i++) { |
174 if (!it.moveNext()) { | 170 if (!it.moveNext()) { |
175 throw new RangeError.range(start, 0, i); | 171 throw new RangeError.range(start, 0, i); |
176 } | 172 } |
177 } | 173 } |
178 List charCodeList; | 174 List charCodeList; |
179 int bits = 0; // Bitwise-or of all char codes in list. | 175 int bits = 0; // Bitwise-or of all char codes in list. |
180 if (end == null) { | 176 if (end == null) { |
181 var list = []; | 177 var list = []; |
182 while (it.moveNext()) { | 178 while (it.moveNext()) { |
183 int code = it.current; | 179 int code = it.current; |
184 bits |= code; | 180 bits |= code; |
185 list.add(code); | 181 list.add(code); |
186 } | 182 } |
187 charCodeList = makeListFixedLength(list); | 183 charCodeList = makeListFixedLength(list); |
188 } else { | 184 } else { |
189 if (end < start) { | 185 if (end < start) { |
(...skipping 29 matching lines...) Expand all Loading... |
219 s._setAt(i, charCodes[start + i]); | 215 s._setAt(i, charCodes[start + i]); |
220 } | 216 } |
221 return s; | 217 return s; |
222 } | 218 } |
223 | 219 |
224 static String _createFromCodePoints(List<int> codePoints, int start, int end) | 220 static String _createFromCodePoints(List<int> codePoints, int start, int end) |
225 native "StringBase_createFromCodePoints"; | 221 native "StringBase_createFromCodePoints"; |
226 | 222 |
227 String operator [](int index) native "String_charAt"; | 223 String operator [](int index) native "String_charAt"; |
228 | 224 |
229 int codeUnitAt(int index); // Implemented in the subclasses. | 225 int codeUnitAt(int index); // Implemented in the subclasses. |
230 | 226 |
231 int get length native "String_getLength"; | 227 int get length native "String_getLength"; |
232 | 228 |
233 bool get isEmpty { | 229 bool get isEmpty { |
234 return this.length == 0; | 230 return this.length == 0; |
235 } | 231 } |
236 | 232 |
237 bool get isNotEmpty => !isEmpty; | 233 bool get isNotEmpty => !isEmpty; |
238 | 234 |
239 String operator +(String other) native "String_concat"; | 235 String operator +(String other) native "String_concat"; |
240 | 236 |
241 String toString() { | 237 String toString() { |
242 return this; | 238 return this; |
243 } | 239 } |
244 | 240 |
245 bool operator ==(Object other) { | 241 bool operator ==(Object other) { |
246 if (identical(this, other)) { | 242 if (identical(this, other)) { |
247 return true; | 243 return true; |
248 } | 244 } |
249 if ((other is! String) || (this.length != other.length)) { | 245 if ((other is! String) || |
| 246 (this.length != other.length)) { |
250 return false; | 247 return false; |
251 } | 248 } |
252 final len = this.length; | 249 final len = this.length; |
253 for (int i = 0; i < len; i++) { | 250 for (int i = 0; i < len; i++) { |
254 if (this.codeUnitAt(i) != other.codeUnitAt(i)) { | 251 if (this.codeUnitAt(i) != other.codeUnitAt(i)) { |
255 return false; | 252 return false; |
256 } | 253 } |
257 } | 254 } |
258 return true; | 255 return true; |
259 } | 256 } |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 } | 382 } |
386 return _substringUncheckedNative(startIndex, endIndex); | 383 return _substringUncheckedNative(startIndex, endIndex); |
387 } | 384 } |
388 | 385 |
389 String _substringUncheckedNative(int startIndex, int endIndex) | 386 String _substringUncheckedNative(int startIndex, int endIndex) |
390 native "StringBase_substringUnchecked"; | 387 native "StringBase_substringUnchecked"; |
391 | 388 |
392 // Checks for one-byte whitespaces only. | 389 // Checks for one-byte whitespaces only. |
393 static bool _isOneByteWhitespace(int codeUnit) { | 390 static bool _isOneByteWhitespace(int codeUnit) { |
394 if (codeUnit <= 32) { | 391 if (codeUnit <= 32) { |
395 return ((codeUnit == 32) || // Space. | 392 return ((codeUnit == 32) || // Space. |
396 ((codeUnit <= 13) && (codeUnit >= 9))); // CR, LF, TAB, etc. | 393 ((codeUnit <= 13) && (codeUnit >= 9))); // CR, LF, TAB, etc. |
397 } | 394 } |
398 return (codeUnit == 0x85) || (codeUnit == 0xA0); // NEL, NBSP. | 395 return (codeUnit == 0x85) || (codeUnit == 0xA0); // NEL, NBSP. |
399 } | 396 } |
400 | 397 |
401 // Characters with Whitespace property (Unicode 6.2). | 398 // Characters with Whitespace property (Unicode 6.2). |
402 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D> | 399 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D> |
403 // 0020 ; White_Space # Zs SPACE | 400 // 0020 ; White_Space # Zs SPACE |
404 // 0085 ; White_Space # Cc <control-0085> | 401 // 0085 ; White_Space # Cc <control-0085> |
405 // 00A0 ; White_Space # Zs NO-BREAK SPACE | 402 // 00A0 ; White_Space # Zs NO-BREAK SPACE |
406 // 1680 ; White_Space # Zs OGHAM SPACE MARK | 403 // 1680 ; White_Space # Zs OGHAM SPACE MARK |
407 // 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR | 404 // 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR |
408 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE | 405 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE |
409 // 2028 ; White_Space # Zl LINE SEPARATOR | 406 // 2028 ; White_Space # Zl LINE SEPARATOR |
410 // 2029 ; White_Space # Zp PARAGRAPH SEPARATOR | 407 // 2029 ; White_Space # Zp PARAGRAPH SEPARATOR |
411 // 202F ; White_Space # Zs NARROW NO-BREAK SPACE | 408 // 202F ; White_Space # Zs NARROW NO-BREAK SPACE |
412 // 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE | 409 // 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE |
413 // 3000 ; White_Space # Zs IDEOGRAPHIC SPACE | 410 // 3000 ; White_Space # Zs IDEOGRAPHIC SPACE |
414 // | 411 // |
415 // BOM: 0xFEFF | 412 // BOM: 0xFEFF |
416 static bool _isTwoByteWhitespace(int codeUnit) { | 413 static bool _isTwoByteWhitespace(int codeUnit) { |
417 if (codeUnit <= 32) { | 414 if (codeUnit <= 32) { |
418 return (codeUnit == 32) || ((codeUnit <= 13) && (codeUnit >= 9)); | 415 return (codeUnit == 32) || |
| 416 ((codeUnit <= 13) && (codeUnit >= 9)); |
419 } | 417 } |
420 if (codeUnit < 0x85) return false; | 418 if (codeUnit < 0x85) return false; |
421 if ((codeUnit == 0x85) || (codeUnit == 0xA0)) return true; | 419 if ((codeUnit == 0x85) || (codeUnit == 0xA0)) return true; |
422 return (codeUnit <= 0x200A) | 420 return (codeUnit <= 0x200A) |
423 ? ((codeUnit == 0x1680) || (codeUnit == 0x180E) || (0x2000 <= codeUnit)) | 421 ? ((codeUnit == 0x1680) || |
424 : ((codeUnit == 0x2028) || | 422 (codeUnit == 0x180E) || |
425 (codeUnit == 0x2029) || | 423 (0x2000 <= codeUnit)) |
426 (codeUnit == 0x202F) || | 424 : ((codeUnit == 0x2028) || |
427 (codeUnit == 0x205F) || | 425 (codeUnit == 0x2029) || |
428 (codeUnit == 0x3000) || | 426 (codeUnit == 0x202F) || |
429 (codeUnit == 0xFEFF)); | 427 (codeUnit == 0x205F) || |
| 428 (codeUnit == 0x3000) || |
| 429 (codeUnit == 0xFEFF)); |
430 } | 430 } |
431 | 431 |
432 int _firstNonWhitespace() { | 432 int _firstNonWhitespace() { |
433 final len = this.length; | 433 final len = this.length; |
434 int first = 0; | 434 int first = 0; |
435 for (; first < len; first++) { | 435 for (; first < len; first++) { |
436 if (!_isWhitespace(this.codeUnitAt(first))) { | 436 if (!_isWhitespace(this.codeUnitAt(first))) { |
437 break; | 437 break; |
438 } | 438 } |
439 } | 439 } |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 // String contains only whitespaces. | 498 // String contains only whitespaces. |
499 return ""; | 499 return ""; |
500 } | 500 } |
501 if (last == (len - 1)) { | 501 if (last == (len - 1)) { |
502 // Returns this string since it does not have trailing whitespaces. | 502 // Returns this string since it does not have trailing whitespaces. |
503 return this; | 503 return this; |
504 } | 504 } |
505 return _substringUnchecked(0, last + 1); | 505 return _substringUnchecked(0, last + 1); |
506 } | 506 } |
507 | 507 |
508 String operator *(int times) { | 508 String operator*(int times) { |
509 if (times <= 0) return ""; | 509 if (times <= 0) return ""; |
510 if (times == 1) return this; | 510 if (times == 1) return this; |
511 StringBuffer buffer = new StringBuffer(this); | 511 StringBuffer buffer = new StringBuffer(this); |
512 for (int i = 1; i < times; i++) { | 512 for (int i = 1; i < times; i++) { |
513 buffer.write(this); | 513 buffer.write(this); |
514 } | 514 } |
515 return buffer.toString(); | 515 return buffer.toString(); |
516 } | 516 } |
517 | 517 |
518 String padLeft(int width, [String padding = ' ']) { | 518 String padLeft(int width, [String padding = ' ']) { |
(...skipping 20 matching lines...) Expand all Loading... |
539 bool contains(Pattern pattern, [int startIndex = 0]) { | 539 bool contains(Pattern pattern, [int startIndex = 0]) { |
540 if (pattern is String) { | 540 if (pattern is String) { |
541 if (startIndex < 0 || startIndex > this.length) { | 541 if (startIndex < 0 || startIndex > this.length) { |
542 throw new RangeError.range(startIndex, 0, this.length); | 542 throw new RangeError.range(startIndex, 0, this.length); |
543 } | 543 } |
544 return indexOf(pattern, startIndex) >= 0; | 544 return indexOf(pattern, startIndex) >= 0; |
545 } | 545 } |
546 return pattern.allMatches(this.substring(startIndex)).isNotEmpty; | 546 return pattern.allMatches(this.substring(startIndex)).isNotEmpty; |
547 } | 547 } |
548 | 548 |
549 String replaceFirst(Pattern pattern, String replacement, | 549 String replaceFirst(Pattern pattern, |
550 [int startIndex = 0]) { | 550 String replacement, |
| 551 [int startIndex = 0]) { |
551 if (pattern is! Pattern) { | 552 if (pattern is! Pattern) { |
552 throw new ArgumentError("${pattern} is not a Pattern"); | 553 throw new ArgumentError("${pattern} is not a Pattern"); |
553 } | 554 } |
554 if (replacement is! String) { | 555 if (replacement is! String) { |
555 throw new ArgumentError("${replacement} is not a String"); | 556 throw new ArgumentError("${replacement} is not a String"); |
556 } | 557 } |
557 if (startIndex is! int) { | 558 if (startIndex is! int) { |
558 throw new ArgumentError("${startIndex} is not an int"); | 559 throw new ArgumentError("${startIndex} is not an int"); |
559 } | 560 } |
560 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 561 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
561 Iterator iterator = startIndex == 0 | 562 Iterator iterator = |
562 ? pattern.allMatches(this).iterator | 563 startIndex == 0 ? pattern.allMatches(this).iterator |
563 : pattern.allMatches(this, startIndex).iterator; | 564 : pattern.allMatches(this, startIndex).iterator; |
564 if (!iterator.moveNext()) return this; | 565 if (!iterator.moveNext()) return this; |
565 Match match = iterator.current; | 566 Match match = iterator.current; |
566 return replaceRange(match.start, match.end, replacement); | 567 return replaceRange(match.start, match.end, replacement); |
567 } | 568 } |
568 | 569 |
569 String replaceRange(int start, int end, String replacement) { | 570 String replaceRange(int start, int end, String replacement) { |
570 int length = this.length; | 571 int length = this.length; |
571 end = RangeError.checkValidRange(start, end, length); | 572 end = RangeError.checkValidRange(start, end, length); |
572 bool replacementIsOneByte = replacement._isOneByte; | 573 bool replacementIsOneByte = replacement._isOneByte; |
573 if (start == 0 && end == length) return replacement; | 574 if (start == 0 && end == length) return replacement; |
574 int replacementLength = replacement.length; | 575 int replacementLength = replacement.length; |
575 int totalLength = start + (length - end) + replacementLength; | 576 int totalLength = start + (length - end) + replacementLength; |
576 if (replacementIsOneByte && this._isOneByte) { | 577 if (replacementIsOneByte && this._isOneByte) { |
577 var result = _OneByteString._allocate(totalLength); | 578 var result = _OneByteString._allocate(totalLength); |
578 int index = 0; | 579 int index = 0; |
579 index = result._setRange(index, this, 0, start); | 580 index = result._setRange(index, this, 0, start); |
580 index = result._setRange(start, replacement, 0, replacementLength); | 581 index = result._setRange(start, replacement, 0, replacementLength); |
581 result._setRange(index, this, end, length); | 582 result._setRange(index, this, end, length); |
582 return result; | 583 return result; |
583 } | 584 } |
584 List slices = []; | 585 List slices = []; |
585 _addReplaceSlice(slices, 0, start); | 586 _addReplaceSlice(slices, 0, start); |
586 if (replacement.length > 0) slices.add(replacement); | 587 if (replacement.length > 0) slices.add(replacement); |
587 _addReplaceSlice(slices, end, length); | 588 _addReplaceSlice(slices, end, length); |
588 return _joinReplaceAllResult( | 589 return _joinReplaceAllResult(this, slices, totalLength, |
589 this, slices, totalLength, replacementIsOneByte); | 590 replacementIsOneByte); |
590 } | 591 } |
591 | 592 |
592 static int _addReplaceSlice(List matches, int start, int end) { | 593 static int _addReplaceSlice(List matches, int start, int end) { |
593 int length = end - start; | 594 int length = end - start; |
594 if (length > 0) { | 595 if (length > 0) { |
595 if (length <= _maxLengthValue && start <= _maxStartValue) { | 596 if (length <= _maxLengthValue && start <= _maxStartValue) { |
596 matches.add(-((start << _lengthBits) | length)); | 597 matches.add(-((start << _lengthBits) | length)); |
597 } else { | 598 } else { |
598 matches.add(start); | 599 matches.add(start); |
599 matches.add(end); | 600 matches.add(end); |
(...skipping 24 matching lines...) Expand all Loading... |
624 } | 625 } |
625 } | 626 } |
626 length += _addReplaceSlice(matches, startIndex, this.length); | 627 length += _addReplaceSlice(matches, startIndex, this.length); |
627 bool replacementIsOneByte = replacement._isOneByte; | 628 bool replacementIsOneByte = replacement._isOneByte; |
628 if (replacementIsOneByte && | 629 if (replacementIsOneByte && |
629 length < _maxJoinReplaceOneByteStringLength && | 630 length < _maxJoinReplaceOneByteStringLength && |
630 this._isOneByte) { | 631 this._isOneByte) { |
631 // TODO(lrn): Is there a cut-off point, or is runtime always faster? | 632 // TODO(lrn): Is there a cut-off point, or is runtime always faster? |
632 return _joinReplaceAllOneByteResult(this, matches, length); | 633 return _joinReplaceAllOneByteResult(this, matches, length); |
633 } | 634 } |
634 return _joinReplaceAllResult(this, matches, length, replacementIsOneByte); | 635 return _joinReplaceAllResult(this, matches, length, |
| 636 replacementIsOneByte); |
635 } | 637 } |
636 | 638 |
637 /** | 639 /** |
638 * As [_joinReplaceAllResult], but knowing that the result | 640 * As [_joinReplaceAllResult], but knowing that the result |
639 * is always a [_OneByteString]. | 641 * is always a [_OneByteString]. |
640 */ | 642 */ |
641 static String _joinReplaceAllOneByteResult( | 643 static String _joinReplaceAllOneByteResult(String base, |
642 String base, List matches, int length) { | 644 List matches, |
| 645 int length) { |
643 _OneByteString result = _OneByteString._allocate(length); | 646 _OneByteString result = _OneByteString._allocate(length); |
644 int writeIndex = 0; | 647 int writeIndex = 0; |
645 for (int i = 0; i < matches.length; i++) { | 648 for (int i = 0; i < matches.length; i++) { |
646 var entry = matches[i]; | 649 var entry = matches[i]; |
647 if (entry is _Smi) { | 650 if (entry is _Smi) { |
648 int sliceStart = entry; | 651 int sliceStart = entry; |
649 int sliceEnd; | 652 int sliceEnd; |
650 if (sliceStart < 0) { | 653 if (sliceStart < 0) { |
651 int bits = -sliceStart; | 654 int bits = -sliceStart; |
652 int sliceLength = bits & _lengthMask; | 655 int sliceLength = bits & _lengthMask; |
(...skipping 26 matching lines...) Expand all Loading... |
679 * Combine the results of a [replaceAll] match into a new string. | 682 * Combine the results of a [replaceAll] match into a new string. |
680 * | 683 * |
681 * The [matches] lists contains Smi index pairs representing slices of | 684 * The [matches] lists contains Smi index pairs representing slices of |
682 * [base] and [String]s to be put in between the slices. | 685 * [base] and [String]s to be put in between the slices. |
683 * | 686 * |
684 * The total [length] of the resulting string is known, as is | 687 * The total [length] of the resulting string is known, as is |
685 * whether the replacement strings are one-byte strings. | 688 * whether the replacement strings are one-byte strings. |
686 * If they are, then we have to check the base string slices to know | 689 * If they are, then we have to check the base string slices to know |
687 * whether the result must be a one-byte string. | 690 * whether the result must be a one-byte string. |
688 */ | 691 */ |
689 static String | 692 static String _joinReplaceAllResult(String base, List matches, int length, |
690 _joinReplaceAllResult(String base, List matches, int length, | 693 bool replacementStringsAreOneByte) |
691 bool replacementStringsAreOneByte) | |
692 native "StringBase_joinReplaceAllResult"; | 694 native "StringBase_joinReplaceAllResult"; |
693 | 695 |
694 String replaceAllMapped(Pattern pattern, String replace(Match match)) { | 696 String replaceAllMapped(Pattern pattern, String replace(Match match)) { |
695 if (pattern == null) throw new ArgumentError.notNull("pattern"); | 697 if (pattern == null) throw new ArgumentError.notNull("pattern"); |
696 if (replace == null) throw new ArgumentError.notNull("replace"); | 698 if (replace == null) throw new ArgumentError.notNull("replace"); |
697 List matches = []; | 699 List matches = []; |
698 int length = 0; | 700 int length = 0; |
699 int startIndex = 0; | 701 int startIndex = 0; |
700 bool replacementStringsAreOneByte = true; | 702 bool replacementStringsAreOneByte = true; |
701 for (Match match in pattern.allMatches(this)) { | 703 for (Match match in pattern.allMatches(this)) { |
702 length += _addReplaceSlice(matches, startIndex, match.start); | 704 length += _addReplaceSlice(matches, startIndex, match.start); |
703 var replacement = "${replace(match)}"; | 705 var replacement = "${replace(match)}"; |
704 matches.add(replacement); | 706 matches.add(replacement); |
705 length += replacement.length; | 707 length += replacement.length; |
706 replacementStringsAreOneByte = | 708 replacementStringsAreOneByte = |
707 replacementStringsAreOneByte && replacement._isOneByte; | 709 replacementStringsAreOneByte && replacement._isOneByte; |
708 startIndex = match.end; | 710 startIndex = match.end; |
709 } | 711 } |
710 if (matches.isEmpty) return this; | 712 if (matches.isEmpty) return this; |
711 length += _addReplaceSlice(matches, startIndex, this.length); | 713 length += _addReplaceSlice(matches, startIndex, this.length); |
712 if (replacementStringsAreOneByte && | 714 if (replacementStringsAreOneByte && |
713 length < _maxJoinReplaceOneByteStringLength && | 715 length < _maxJoinReplaceOneByteStringLength && |
714 this._isOneByte) { | 716 this._isOneByte) { |
715 return _joinReplaceAllOneByteResult(this, matches, length); | 717 return _joinReplaceAllOneByteResult(this, matches, length); |
716 } | 718 } |
717 return _joinReplaceAllResult( | 719 return _joinReplaceAllResult(this, matches, length, |
718 this, matches, length, replacementStringsAreOneByte); | 720 replacementStringsAreOneByte); |
719 } | 721 } |
720 | 722 |
721 String replaceFirstMapped(Pattern pattern, String replace(Match match), | 723 String replaceFirstMapped(Pattern pattern, String replace(Match match), |
722 [int startIndex = 0]) { | 724 [int startIndex = 0]) { |
723 if (pattern == null) throw new ArgumentError.notNull("pattern"); | 725 if (pattern == null) throw new ArgumentError.notNull("pattern"); |
724 if (replace == null) throw new ArgumentError.notNull("replace"); | 726 if (replace == null) throw new ArgumentError.notNull("replace"); |
725 if (startIndex == null) throw new ArgumentError.notNull("startIndex"); | 727 if (startIndex == null) throw new ArgumentError.notNull("startIndex"); |
726 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 728 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
727 | 729 |
728 var matches = pattern.allMatches(this, startIndex).iterator; | 730 var matches = pattern.allMatches(this, startIndex).iterator; |
729 if (!matches.moveNext()) return this; | 731 if (!matches.moveNext()) return this; |
730 var match = matches.current; | 732 var match = matches.current; |
731 var replacement = "${replace(match)}"; | 733 var replacement = "${replace(match)}"; |
732 return replaceRange(match.start, match.end, replacement); | 734 return replaceRange(match.start, match.end, replacement); |
733 } | 735 } |
734 | 736 |
735 static String _matchString(Match match) => match[0]; | 737 static String _matchString(Match match) => match[0]; |
736 static String _stringIdentity(String string) => string; | 738 static String _stringIdentity(String string) => string; |
737 | 739 |
738 String _splitMapJoinEmptyString( | 740 String _splitMapJoinEmptyString(String onMatch(Match match), |
739 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 741 String onNonMatch(String nonMatch)) { |
740 // Pattern is the empty string. | 742 // Pattern is the empty string. |
741 StringBuffer buffer = new StringBuffer(); | 743 StringBuffer buffer = new StringBuffer(); |
742 int length = this.length; | 744 int length = this.length; |
743 int i = 0; | 745 int i = 0; |
744 buffer.write(onNonMatch("")); | 746 buffer.write(onNonMatch("")); |
745 while (i < length) { | 747 while (i < length) { |
746 buffer.write(onMatch(new _StringMatch(i, this, ""))); | 748 buffer.write(onMatch(new _StringMatch(i, this, ""))); |
747 // Special case to avoid splitting a surrogate pair. | 749 // Special case to avoid splitting a surrogate pair. |
748 int code = this.codeUnitAt(i); | 750 int code = this.codeUnitAt(i); |
749 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { | 751 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { |
750 // Leading surrogate; | 752 // Leading surrogate; |
751 code = this.codeUnitAt(i + 1); | 753 code = this.codeUnitAt(i + 1); |
752 if ((code & ~0x3FF) == 0xDC00) { | 754 if ((code & ~0x3FF) == 0xDC00) { |
753 // Matching trailing surrogate. | 755 // Matching trailing surrogate. |
754 buffer.write(onNonMatch(this.substring(i, i + 2))); | 756 buffer.write(onNonMatch(this.substring(i, i + 2))); |
755 i += 2; | 757 i += 2; |
756 continue; | 758 continue; |
757 } | 759 } |
758 } | 760 } |
759 buffer.write(onNonMatch(this[i])); | 761 buffer.write(onNonMatch(this[i])); |
760 i++; | 762 i++; |
761 } | 763 } |
762 buffer.write(onMatch(new _StringMatch(i, this, ""))); | 764 buffer.write(onMatch(new _StringMatch(i, this, ""))); |
763 buffer.write(onNonMatch("")); | 765 buffer.write(onNonMatch("")); |
764 return buffer.toString(); | 766 return buffer.toString(); |
765 } | 767 } |
766 | 768 |
767 String splitMapJoin(Pattern pattern, | 769 String splitMapJoin(Pattern pattern, |
768 {String onMatch(Match match), String onNonMatch(String nonMatch)}) { | 770 {String onMatch(Match match), |
| 771 String onNonMatch(String nonMatch)}) { |
769 if (pattern is! Pattern) { | 772 if (pattern is! Pattern) { |
770 throw new ArgumentError("${pattern} is not a Pattern"); | 773 throw new ArgumentError("${pattern} is not a Pattern"); |
771 } | 774 } |
772 if (onMatch == null) onMatch = _matchString; | 775 if (onMatch == null) onMatch = _matchString; |
773 if (onNonMatch == null) onNonMatch = _stringIdentity; | 776 if (onNonMatch == null) onNonMatch = _stringIdentity; |
774 if (pattern is String) { | 777 if (pattern is String) { |
775 String stringPattern = pattern; | 778 String stringPattern = pattern; |
776 if (stringPattern.isEmpty) { | 779 if (stringPattern.isEmpty) { |
777 return _splitMapJoinEmptyString(onMatch, onNonMatch); | 780 return _splitMapJoinEmptyString(onMatch, onNonMatch); |
778 } | 781 } |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
877 result.add(this.substring(previousIndex, length)); | 880 result.add(this.substring(previousIndex, length)); |
878 break; | 881 break; |
879 } | 882 } |
880 Match match = iterator.current; | 883 Match match = iterator.current; |
881 if (match.start == length) { | 884 if (match.start == length) { |
882 result.add(this.substring(previousIndex, length)); | 885 result.add(this.substring(previousIndex, length)); |
883 break; | 886 break; |
884 } | 887 } |
885 int endIndex = match.end; | 888 int endIndex = match.end; |
886 if (startIndex == endIndex && endIndex == previousIndex) { | 889 if (startIndex == endIndex && endIndex == previousIndex) { |
887 ++startIndex; // empty match, advance and restart | 890 ++startIndex; // empty match, advance and restart |
888 continue; | 891 continue; |
889 } | 892 } |
890 result.add(this.substring(previousIndex, match.start)); | 893 result.add(this.substring(previousIndex, match.start)); |
891 startIndex = previousIndex = endIndex; | 894 startIndex = previousIndex = endIndex; |
892 } | 895 } |
893 return result; | 896 return result; |
894 } | 897 } |
895 | 898 |
896 List<int> get codeUnits => new CodeUnits(this); | 899 List<int> get codeUnits => new CodeUnits(this); |
897 | 900 |
(...skipping 11 matching lines...) Expand all Loading... |
909 } | 912 } |
910 return _concatRangeNative(strings, start, end); | 913 return _concatRangeNative(strings, start, end); |
911 } | 914 } |
912 | 915 |
913 // Call this method if not all list elements are known to be OneByteString(s). | 916 // Call this method if not all list elements are known to be OneByteString(s). |
914 // 'strings' must be an _List or _GrowableList. | 917 // 'strings' must be an _List or _GrowableList. |
915 static String _concatRangeNative(List<String> strings, int start, int end) | 918 static String _concatRangeNative(List<String> strings, int start, int end) |
916 native "String_concatRange"; | 919 native "String_concatRange"; |
917 } | 920 } |
918 | 921 |
| 922 |
919 class _OneByteString extends _StringBase implements String { | 923 class _OneByteString extends _StringBase implements String { |
| 924 |
920 factory _OneByteString._uninstantiable() { | 925 factory _OneByteString._uninstantiable() { |
921 throw new UnsupportedError( | 926 throw new UnsupportedError( |
922 "_OneByteString can only be allocated by the VM"); | 927 "_OneByteString can only be allocated by the VM"); |
923 } | 928 } |
924 | 929 |
925 int get hashCode native "String_getHashCode"; | 930 int get hashCode native "String_getHashCode"; |
926 | 931 |
927 int codeUnitAt(int index) native "String_codeUnitAt"; | 932 int codeUnitAt(int index) native "String_codeUnitAt"; |
928 | 933 |
929 bool _isWhitespace(int codeUnit) { | 934 bool _isWhitespace(int codeUnit) { |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1006 if (this.codeUnitAt(i) == patternCu0) { | 1011 if (this.codeUnitAt(i) == patternCu0) { |
1007 return true; | 1012 return true; |
1008 } | 1013 } |
1009 } | 1014 } |
1010 return false; | 1015 return false; |
1011 } | 1016 } |
1012 } | 1017 } |
1013 return super.contains(pattern, start); | 1018 return super.contains(pattern, start); |
1014 } | 1019 } |
1015 | 1020 |
1016 String operator *(int times) { | 1021 String operator*(int times) { |
1017 if (times <= 0) return ""; | 1022 if (times <= 0) return ""; |
1018 if (times == 1) return this; | 1023 if (times == 1) return this; |
1019 int length = this.length; | 1024 int length = this.length; |
1020 if (this.isEmpty) return this; // Don't clone empty string. | 1025 if (this.isEmpty) return this; // Don't clone empty string. |
1021 _OneByteString result = _OneByteString._allocate(length * times); | 1026 _OneByteString result = _OneByteString._allocate(length * times); |
1022 int index = 0; | 1027 int index = 0; |
1023 for (int i = 0; i < times; i++) { | 1028 for (int i = 0; i < times; i ++) { |
1024 for (int j = 0; j < length; j++) { | 1029 for (int j = 0; j < length; j++) { |
1025 result._setAt(index++, this.codeUnitAt(j)); | 1030 result._setAt(index++, this.codeUnitAt(j)); |
1026 } | 1031 } |
1027 } | 1032 } |
1028 return result; | 1033 return result; |
1029 } | 1034 } |
1030 | 1035 |
1031 String padLeft(int width, [String padding = ' ']) { | 1036 String padLeft(int width, [String padding = ' ']) { |
1032 int padCid = ClassID.getID(padding); | 1037 int padCid = ClassID.getID(padding); |
1033 if ((padCid != ClassID.cidOneByteString) && | 1038 if ((padCid != ClassID.cidOneByteString) && |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1181 } | 1186 } |
1182 return result; | 1187 return result; |
1183 } | 1188 } |
1184 return this; | 1189 return this; |
1185 } | 1190 } |
1186 | 1191 |
1187 // Allocates a string of given length, expecting its content to be | 1192 // Allocates a string of given length, expecting its content to be |
1188 // set using _setAt. | 1193 // set using _setAt. |
1189 static _OneByteString _allocate(int length) native "OneByteString_allocate"; | 1194 static _OneByteString _allocate(int length) native "OneByteString_allocate"; |
1190 | 1195 |
1191 static _OneByteString _allocateFromOneByteList(List<int> list, int start, | 1196 |
1192 int end) native "OneByteString_allocateFromOneByteList"; | 1197 static _OneByteString _allocateFromOneByteList(List<int> list, |
| 1198 int start, int end) |
| 1199 native "OneByteString_allocateFromOneByteList"; |
1193 | 1200 |
1194 // This is internal helper method. Code point value must be a valid | 1201 // This is internal helper method. Code point value must be a valid |
1195 // Latin1 value (0..0xFF), index must be valid. | 1202 // Latin1 value (0..0xFF), index must be valid. |
1196 void _setAt(int index, int codePoint) native "OneByteString_setAt"; | 1203 void _setAt(int index, int codePoint) native "OneByteString_setAt"; |
1197 | 1204 |
1198 // Should be optimizable to a memory move. | 1205 // Should be optimizable to a memory move. |
1199 // Accepts both _OneByteString and _ExternalOneByteString as argument. | 1206 // Accepts both _OneByteString and _ExternalOneByteString as argument. |
1200 // Returns index after last character written. | 1207 // Returns index after last character written. |
1201 int _setRange(int index, String oneByteString, int start, int end) { | 1208 int _setRange(int index, String oneByteString, int start, int end) { |
1202 assert(oneByteString._isOneByte); | 1209 assert(oneByteString._isOneByte); |
1203 assert(0 <= start); | 1210 assert(0 <= start); |
1204 assert(start <= end); | 1211 assert(start <= end); |
1205 assert(end <= oneByteString.length); | 1212 assert(end <= oneByteString.length); |
1206 assert(0 <= index); | 1213 assert(0 <= index); |
1207 assert(index + (end - start) <= length); | 1214 assert(index + (end - start) <= length); |
1208 for (int i = start; i < end; i++) { | 1215 for (int i = start; i < end; i++) { |
1209 _setAt(index, oneByteString.codeUnitAt(i)); | 1216 _setAt(index, oneByteString.codeUnitAt(i)); |
1210 index += 1; | 1217 index += 1; |
1211 } | 1218 } |
1212 return index; | 1219 return index; |
1213 } | 1220 } |
1214 } | 1221 } |
1215 | 1222 |
| 1223 |
1216 class _TwoByteString extends _StringBase implements String { | 1224 class _TwoByteString extends _StringBase implements String { |
1217 factory _TwoByteString._uninstantiable() { | 1225 factory _TwoByteString._uninstantiable() { |
1218 throw new UnsupportedError( | 1226 throw new UnsupportedError( |
1219 "_TwoByteString can only be allocated by the VM"); | 1227 "_TwoByteString can only be allocated by the VM"); |
1220 } | 1228 } |
1221 | 1229 |
1222 static String _allocateFromTwoByteList(List list, int start, int end) | 1230 static String _allocateFromTwoByteList(List list, int start, int end) |
1223 native "TwoByteString_allocateFromTwoByteList"; | 1231 native "TwoByteString_allocateFromTwoByteList"; |
1224 | 1232 |
1225 bool _isWhitespace(int codeUnit) { | 1233 bool _isWhitespace(int codeUnit) { |
1226 return _StringBase._isTwoByteWhitespace(codeUnit); | 1234 return _StringBase._isTwoByteWhitespace(codeUnit); |
1227 } | 1235 } |
1228 | 1236 |
1229 int codeUnitAt(int index) native "String_codeUnitAt"; | 1237 int codeUnitAt(int index) native "String_codeUnitAt"; |
1230 | 1238 |
1231 bool operator ==(Object other) { | 1239 bool operator ==(Object other) { |
1232 return super == other; | 1240 return super == other; |
1233 } | 1241 } |
1234 } | 1242 } |
1235 | 1243 |
| 1244 |
1236 class _ExternalOneByteString extends _StringBase implements String { | 1245 class _ExternalOneByteString extends _StringBase implements String { |
1237 factory _ExternalOneByteString._uninstantiable() { | 1246 factory _ExternalOneByteString._uninstantiable() { |
1238 throw new UnsupportedError( | 1247 throw new UnsupportedError( |
1239 "_ExternalOneByteString can only be allocated by the VM"); | 1248 "_ExternalOneByteString can only be allocated by the VM"); |
1240 } | 1249 } |
1241 | 1250 |
1242 bool _isWhitespace(int codeUnit) { | 1251 bool _isWhitespace(int codeUnit) { |
1243 return _StringBase._isOneByteWhitespace(codeUnit); | 1252 return _StringBase._isOneByteWhitespace(codeUnit); |
1244 } | 1253 } |
1245 | 1254 |
1246 int codeUnitAt(int index) native "String_codeUnitAt"; | 1255 int codeUnitAt(int index) native "String_codeUnitAt"; |
1247 | 1256 |
1248 bool operator ==(Object other) { | 1257 bool operator ==(Object other) { |
1249 return super == other; | 1258 return super == other; |
1250 } | 1259 } |
1251 | 1260 |
1252 static int _getCid() native "ExternalOneByteString_getCid"; | 1261 static int _getCid() native "ExternalOneByteString_getCid"; |
1253 } | 1262 } |
1254 | 1263 |
| 1264 |
1255 class _ExternalTwoByteString extends _StringBase implements String { | 1265 class _ExternalTwoByteString extends _StringBase implements String { |
1256 factory _ExternalTwoByteString._uninstantiable() { | 1266 factory _ExternalTwoByteString._uninstantiable() { |
1257 throw new UnsupportedError( | 1267 throw new UnsupportedError( |
1258 "_ExternalTwoByteString can only be allocated by the VM"); | 1268 "_ExternalTwoByteString can only be allocated by the VM"); |
1259 } | 1269 } |
1260 | 1270 |
1261 bool _isWhitespace(int codeUnit) { | 1271 bool _isWhitespace(int codeUnit) { |
1262 return _StringBase._isTwoByteWhitespace(codeUnit); | 1272 return _StringBase._isTwoByteWhitespace(codeUnit); |
1263 } | 1273 } |
1264 | 1274 |
1265 int codeUnitAt(int index) native "String_codeUnitAt"; | 1275 int codeUnitAt(int index) native "String_codeUnitAt"; |
1266 | 1276 |
1267 bool operator ==(Object other) { | 1277 bool operator ==(Object other) { |
1268 return super == other; | 1278 return super == other; |
1269 } | 1279 } |
1270 } | 1280 } |
1271 | 1281 |
| 1282 |
1272 class _StringMatch implements Match { | 1283 class _StringMatch implements Match { |
1273 const _StringMatch(this.start, this.input, this.pattern); | 1284 const _StringMatch(this.start, this.input, this.pattern); |
1274 | 1285 |
1275 int get end => start + pattern.length; | 1286 int get end => start + pattern.length; |
1276 String operator [](int g) => group(g); | 1287 String operator[](int g) => group(g); |
1277 int get groupCount => 0; | 1288 int get groupCount => 0; |
1278 | 1289 |
1279 String group(int group) { | 1290 String group(int group) { |
1280 if (group != 0) { | 1291 if (group != 0) { |
1281 throw new RangeError.value(group); | 1292 throw new RangeError.value(group); |
1282 } | 1293 } |
1283 return pattern; | 1294 return pattern; |
1284 } | 1295 } |
1285 | 1296 |
1286 List<String> groups(List<int> groups) { | 1297 List<String> groups(List<int> groups) { |
1287 List<String> result = new List<String>(); | 1298 List<String> result = new List<String>(); |
1288 for (int g in groups) { | 1299 for (int g in groups) { |
1289 result.add(group(g)); | 1300 result.add(group(g)); |
1290 } | 1301 } |
1291 return result; | 1302 return result; |
1292 } | 1303 } |
1293 | 1304 |
1294 final int start; | 1305 final int start; |
1295 final String input; | 1306 final String input; |
1296 final String pattern; | 1307 final String pattern; |
1297 } | 1308 } |
1298 | 1309 |
| 1310 |
1299 class _StringAllMatchesIterable extends Iterable<Match> { | 1311 class _StringAllMatchesIterable extends Iterable<Match> { |
1300 final String _input; | 1312 final String _input; |
1301 final String _pattern; | 1313 final String _pattern; |
1302 final int _index; | 1314 final int _index; |
1303 | 1315 |
1304 _StringAllMatchesIterable(this._input, this._pattern, this._index); | 1316 _StringAllMatchesIterable(this._input, this._pattern, this._index); |
1305 | 1317 |
1306 Iterator<Match> get iterator => | 1318 Iterator<Match> get iterator => |
1307 new _StringAllMatchesIterator(_input, _pattern, _index); | 1319 new _StringAllMatchesIterator(_input, _pattern, _index); |
1308 | 1320 |
(...skipping 28 matching lines...) Expand all Loading... |
1337 int end = index + _pattern.length; | 1349 int end = index + _pattern.length; |
1338 _current = new _StringMatch(index, _input, _pattern); | 1350 _current = new _StringMatch(index, _input, _pattern); |
1339 // Empty match, don't start at same location again. | 1351 // Empty match, don't start at same location again. |
1340 if (end == _index) end++; | 1352 if (end == _index) end++; |
1341 _index = end; | 1353 _index = end; |
1342 return true; | 1354 return true; |
1343 } | 1355 } |
1344 | 1356 |
1345 Match get current => _current; | 1357 Match get current => _current; |
1346 } | 1358 } |
OLD | NEW |