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