OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of dart._interceptors; | 5 part of dart._interceptors; |
6 | 6 |
7 /** | 7 /** |
8 * The interceptor class for [String]. The compiler recognizes this | 8 * The interceptor class for [String]. The compiler recognizes this |
9 * class as an interceptor, and changes references to [:this:] to | 9 * class as an interceptor, and changes references to [:this:] to |
10 * actually use the receiver of the method, which is generated as an extra | 10 * actually use the receiver of the method, which is generated as an extra |
11 * argument added to each member. | 11 * argument added to each member. |
12 */ | 12 */ |
13 @JsPeerInterface(name: 'String') | 13 @JsPeerInterface(name: 'String') |
14 class JSString extends Interceptor implements String, JSIndexable { | 14 class JSString extends Interceptor implements String, JSIndexable { |
15 const JSString(); | 15 const JSString(); |
16 | 16 |
17 int codeUnitAt(int index) { | 17 int codeUnitAt(int index) { |
18 if (index is !int) throw new ArgumentError(index); | 18 if (index is !int) throw diagnoseIndexError(this, index); |
19 if (index < 0) throw new RangeError.value(index); | 19 if (index < 0) throw diagnoseIndexError(this, index); |
20 if (index >= length) throw new RangeError.value(index); | 20 if (index >= length) throw diagnoseIndexError(this, index); |
21 return JS('int', r'#.charCodeAt(#)', this, index); | 21 return JS('int', r'#.charCodeAt(#)', this, index); |
22 } | 22 } |
23 | 23 |
24 Iterable<Match> allMatches(String string, [int start = 0]) { | 24 Iterable<Match> allMatches(String string, [int start = 0]) { |
25 checkString(string); | 25 checkString(string); |
26 checkInt(start); | 26 checkInt(start); |
27 if (0 > start || start > string.length) { | 27 if (0 > start || start > string.length) { |
28 throw new RangeError.range(start, 0, string.length); | 28 throw new RangeError.range(start, 0, string.length); |
29 } | 29 } |
30 return allMatchesInStringUnchecked(this, string, start); | 30 return allMatchesInStringUnchecked(this, string, start); |
31 } | 31 } |
32 | 32 |
33 Match matchAsPrefix(String string, [int start = 0]) { | 33 Match matchAsPrefix(String string, [int start = 0]) { |
34 if (start < 0 || start > string.length) { | 34 if (start < 0 || start > string.length) { |
35 throw new RangeError.range(start, 0, string.length); | 35 throw new RangeError.range(start, 0, string.length); |
36 } | 36 } |
37 if (start + this.length > string.length) return null; | 37 if (start + this.length > string.length) return null; |
38 // TODO(lrn): See if this can be optimized. | 38 // TODO(lrn): See if this can be optimized. |
39 for (int i = 0; i < this.length; i++) { | 39 for (int i = 0; i < this.length; i++) { |
40 if (string.codeUnitAt(start + i) != this.codeUnitAt(i)) { | 40 if (string.codeUnitAt(start + i) != this.codeUnitAt(i)) { |
41 return null; | 41 return null; |
42 } | 42 } |
43 } | 43 } |
44 return new StringMatch(start, string, this); | 44 return new StringMatch(start, string, this); |
45 } | 45 } |
46 | 46 |
47 String operator +(String other) { | 47 String operator +(String other) { |
48 if (other is !String) throw new ArgumentError(other); | 48 if (other is !String) throw new ArgumentError.value(other); |
49 return JS('String', r'# + #', this, other); | 49 return JS('String', r'# + #', this, other); |
50 } | 50 } |
51 | 51 |
52 bool endsWith(String other) { | 52 bool endsWith(String other) { |
53 checkString(other); | 53 checkString(other); |
54 int otherLength = other.length; | 54 int otherLength = other.length; |
55 if (otherLength > length) return false; | 55 if (otherLength > length) return false; |
56 return other == substring(length - otherLength); | 56 return other == substring(length - otherLength); |
57 } | 57 } |
58 | 58 |
59 String replaceAll(Pattern from, String to) { | 59 String replaceAll(Pattern from, String to) { |
60 checkString(to); | 60 checkString(to); |
61 return stringReplaceAllUnchecked(this, from, to); | 61 return stringReplaceAllUnchecked(this, from, to); |
62 } | 62 } |
63 | 63 |
64 String replaceAllMapped(Pattern from, String convert(Match match)) { | 64 String replaceAllMapped(Pattern from, String convert(Match match)) { |
65 return this.splitMapJoin(from, onMatch: convert); | 65 return this.splitMapJoin(from, onMatch: convert); |
66 } | 66 } |
67 | 67 |
68 String splitMapJoin(Pattern from, | 68 String splitMapJoin(Pattern from, |
69 {String onMatch(Match match), | 69 {String onMatch(Match match), |
70 String onNonMatch(String nonMatch)}) { | 70 String onNonMatch(String nonMatch)}) { |
71 return stringReplaceAllFuncUnchecked(this, from, onMatch, onNonMatch); | 71 return stringReplaceAllFuncUnchecked(this, from, onMatch, onNonMatch); |
72 } | 72 } |
73 | 73 |
74 String replaceFirst(Pattern from, String to, [int startIndex = 0]) { | 74 String replaceFirst(Pattern from, String to, [int startIndex = 0]) { |
75 checkString(to); | 75 checkString(to); |
76 checkInt(startIndex); | 76 checkInt(startIndex); |
77 if (startIndex < 0 || startIndex > this.length) { | 77 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
78 throw new RangeError.range(startIndex, 0, this.length); | |
79 } | |
80 return stringReplaceFirstUnchecked(this, from, to, startIndex); | 78 return stringReplaceFirstUnchecked(this, from, to, startIndex); |
81 } | 79 } |
82 | 80 |
| 81 String replaceFirstMapped(Pattern from, String replace(Match match), |
| 82 [int startIndex = 0]) { |
| 83 checkNull(replace); |
| 84 checkInt(startIndex); |
| 85 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
| 86 return stringReplaceFirstMappedUnchecked(this, from, replace, startIndex); |
| 87 } |
| 88 |
83 List<String> split(Pattern pattern) { | 89 List<String> split(Pattern pattern) { |
84 checkNull(pattern); | 90 checkNull(pattern); |
85 if (pattern is String) { | 91 if (pattern is String) { |
86 return JS('JSExtendableArray', r'#.split(#)', this, pattern); | 92 return JS('JSExtendableArray', r'#.split(#)', this, pattern); |
87 } else if (pattern is JSSyntaxRegExp && regExpCaptureCount(pattern) == 0) { | 93 } else if (pattern is JSSyntaxRegExp && regExpCaptureCount(pattern) == 0) { |
88 var re = regExpGetNative(pattern); | 94 var re = regExpGetNative(pattern); |
89 return JS('JSExtendableArray', r'#.split(#)', this, re); | 95 return JS('JSExtendableArray', r'#.split(#)', this, re); |
90 } else { | 96 } else { |
91 return _defaultSplit(pattern); | 97 return _defaultSplit(pattern); |
92 } | 98 } |
93 } | 99 } |
94 | 100 |
| 101 String replaceRange(int start, int end, String replacement) { |
| 102 checkString(replacement); |
| 103 checkInt(start); |
| 104 end = RangeError.checkValidRange(start, end, this.length); |
| 105 checkInt(end); |
| 106 return stringReplaceRangeUnchecked(this, start, end, replacement); |
| 107 } |
| 108 |
95 List<String> _defaultSplit(Pattern pattern) { | 109 List<String> _defaultSplit(Pattern pattern) { |
96 List<String> result = <String>[]; | 110 List<String> result = <String>[]; |
97 // End of most recent match. That is, start of next part to add to result. | 111 // End of most recent match. That is, start of next part to add to result. |
98 int start = 0; | 112 int start = 0; |
99 // Length of most recent match. | 113 // Length of most recent match. |
100 // Set >0, so no match on the empty string causes the result to be [""]. | 114 // Set >0, so no match on the empty string causes the result to be [""]. |
101 int length = 1; | 115 int length = 1; |
102 for (var match in pattern.allMatches(this)) { | 116 for (var match in pattern.allMatches(this)) { |
103 int matchStart = match.start; | 117 int matchStart = match.start; |
104 int matchEnd = match.end; | 118 int matchEnd = match.end; |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 if (delta <= 0) return this; | 373 if (delta <= 0) return this; |
360 return this + padding * delta; | 374 return this + padding * delta; |
361 } | 375 } |
362 | 376 |
363 List<int> get codeUnits => new _CodeUnits(this); | 377 List<int> get codeUnits => new _CodeUnits(this); |
364 | 378 |
365 Runes get runes => new Runes(this); | 379 Runes get runes => new Runes(this); |
366 | 380 |
367 int indexOf(Pattern pattern, [int start = 0]) { | 381 int indexOf(Pattern pattern, [int start = 0]) { |
368 checkNull(pattern); | 382 checkNull(pattern); |
369 if (start is! int) throw new ArgumentError(start); | 383 if (start is! int) throw argumentErrorValue(start); |
370 if (start < 0 || start > this.length) { | 384 if (start < 0 || start > this.length) { |
371 throw new RangeError.range(start, 0, this.length); | 385 throw new RangeError.range(start, 0, this.length); |
372 } | 386 } |
373 if (pattern is String) { | 387 if (pattern is String) { |
374 return JS('int', r'#.indexOf(#, #)', this, pattern, start); | 388 return stringIndexOfStringUnchecked(this, pattern, start); |
375 } | 389 } |
376 if (pattern is JSSyntaxRegExp) { | 390 if (pattern is JSSyntaxRegExp) { |
377 JSSyntaxRegExp re = pattern; | 391 JSSyntaxRegExp re = pattern; |
378 Match match = firstMatchAfter(re, this, start); | 392 Match match = firstMatchAfter(re, this, start); |
379 return (match == null) ? -1 : match.start; | 393 return (match == null) ? -1 : match.start; |
380 } | 394 } |
381 for (int i = start; i <= this.length; i++) { | 395 for (int i = start; i <= this.length; i++) { |
382 if (pattern.matchAsPrefix(this, i) != null) return i; | 396 if (pattern.matchAsPrefix(this, i) != null) return i; |
383 } | 397 } |
384 return -1; | 398 return -1; |
385 } | 399 } |
386 | 400 |
387 int lastIndexOf(Pattern pattern, [int start]) { | 401 int lastIndexOf(Pattern pattern, [int start]) { |
388 checkNull(pattern); | 402 checkNull(pattern); |
389 if (start == null) { | 403 if (start == null) { |
390 start = length; | 404 start = length; |
391 } else if (start is! int) { | 405 } else if (start is! int) { |
392 throw new ArgumentError(start); | 406 throw argumentErrorValue(start); |
393 } else if (start < 0 || start > this.length) { | 407 } else if (start < 0 || start > this.length) { |
394 throw new RangeError.range(start, 0, this.length); | 408 throw new RangeError.range(start, 0, this.length); |
395 } | 409 } |
396 if (pattern is String) { | 410 if (pattern is String) { |
397 String other = pattern; | 411 String other = pattern; |
398 if (start + other.length > this.length) { | 412 if (start + other.length > this.length) { |
399 start = this.length - other.length; | 413 start = this.length - other.length; |
400 } | 414 } |
401 return stringLastIndexOfUnchecked(this, other, start); | 415 return stringLastIndexOfUnchecked(this, other, start); |
402 } | 416 } |
403 for (int i = start; i >= 0; i--) { | 417 for (int i = start; i >= 0; i--) { |
404 if (pattern.matchAsPrefix(this, i) != null) return i; | 418 if (pattern.matchAsPrefix(this, i) != null) return i; |
405 } | 419 } |
406 return -1; | 420 return -1; |
407 } | 421 } |
408 | 422 |
409 bool contains(Pattern other, [int startIndex = 0]) { | 423 bool contains(Pattern other, [int startIndex = 0]) { |
410 checkNull(other); | 424 checkNull(other); |
411 if (startIndex < 0 || startIndex > this.length) { | 425 if (startIndex < 0 || startIndex > this.length) { |
412 throw new RangeError.range(startIndex, 0, this.length); | 426 throw new RangeError.range(startIndex, 0, this.length); |
413 } | 427 } |
414 return stringContainsUnchecked(this, other, startIndex); | 428 return stringContainsUnchecked(this, other, startIndex); |
415 } | 429 } |
416 | 430 |
417 bool get isEmpty => length == 0; | 431 bool get isEmpty => length == 0; |
418 | 432 |
419 bool get isNotEmpty => !isEmpty; | 433 bool get isNotEmpty => !isEmpty; |
420 | 434 |
421 int compareTo(String other) { | 435 int compareTo(String other) { |
422 if (other is !String) throw new ArgumentError(other); | 436 if (other is !String) throw argumentErrorValue(other); |
423 return this == other ? 0 | 437 return this == other ? 0 |
424 : JS('bool', r'# < #', this, other) ? -1 : 1; | 438 : JS('bool', r'# < #', this, other) ? -1 : 1; |
425 } | 439 } |
426 | 440 |
427 // Note: if you change this, also change the function [S]. | 441 // Note: if you change this, also change the function [S]. |
428 String toString() => this; | 442 String toString() => this; |
429 | 443 |
430 /** | 444 /** |
431 * This is the [Jenkins hash function][1] but using masking to keep | 445 * This is the [Jenkins hash function][1] but using masking to keep |
432 * values in SMI range. | 446 * values in SMI range. |
433 * | 447 * |
434 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function | 448 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function |
(...skipping 10 matching lines...) Expand all Loading... |
445 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | 459 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
446 hash = JS('int', '# ^ (# >> 11)', hash, hash); | 460 hash = JS('int', '# ^ (# >> 11)', hash, hash); |
447 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | 461 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
448 } | 462 } |
449 | 463 |
450 Type get runtimeType => String; | 464 Type get runtimeType => String; |
451 | 465 |
452 int get length => JS('int', r'#.length', this); | 466 int get length => JS('int', r'#.length', this); |
453 | 467 |
454 String operator [](int index) { | 468 String operator [](int index) { |
455 if (index is !int) throw new ArgumentError(index); | 469 if (index is !int) throw diagnoseIndexError(this, index); |
456 if (index >= length || index < 0) throw new RangeError.value(index); | 470 if (index >= length || index < 0) throw diagnoseIndexError(this, index); |
457 return JS('String', '#[#]', this, index); | 471 return JS('String', '#[#]', this, index); |
458 } | 472 } |
459 } | 473 } |
460 | 474 |
461 /** | 475 /** |
462 * An [Iterable] of the UTF-16 code units of a [String] in index order. | 476 * An [Iterable] of the UTF-16 code units of a [String] in index order. |
463 */ | 477 */ |
464 class _CodeUnits extends UnmodifiableListBase<int> { | 478 class _CodeUnits extends UnmodifiableListBase<int> { |
465 /** The string that this is the code units of. */ | 479 /** The string that this is the code units of. */ |
466 String _string; | 480 String _string; |
467 | 481 |
468 _CodeUnits(this._string); | 482 _CodeUnits(this._string); |
469 | 483 |
470 int get length => _string.length; | 484 int get length => _string.length; |
471 int operator[](int i) => _string.codeUnitAt(i); | 485 int operator[](int i) => _string.codeUnitAt(i); |
472 } | 486 } |
OLD | NEW |