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 /** | 5 /** |
6 * [_StringBase] contains common methods used by concrete String | 6 * [_StringBase] contains common methods used by concrete String |
7 * implementations, e.g., _OneByteString. | 7 * implementations, e.g., _OneByteString. |
8 */ | 8 */ |
9 class _StringBase { | 9 class _StringBase { |
10 | 10 |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 } | 148 } |
149 if ((endIndex < 0) || (endIndex > this.length)) { | 149 if ((endIndex < 0) || (endIndex > this.length)) { |
150 throw new RangeError.value(endIndex); | 150 throw new RangeError.value(endIndex); |
151 } | 151 } |
152 if (startIndex > endIndex) { | 152 if (startIndex > endIndex) { |
153 throw new RangeError.value(startIndex); | 153 throw new RangeError.value(startIndex); |
154 } | 154 } |
155 return _substringUnchecked(startIndex, endIndex); | 155 return _substringUnchecked(startIndex, endIndex); |
156 } | 156 } |
157 | 157 |
| 158 String slice([int startIndex, int endIndex]) { |
| 159 int start, end; |
| 160 if (startIndex == null) { |
| 161 start = 0; |
| 162 } else if (startIndex is! int) { |
| 163 throw new ArgumentError("startIndex is not int"); |
| 164 } else if (startIndex >= 0) { |
| 165 start = startIndex; |
| 166 } else { |
| 167 start = this.length + startIndex; |
| 168 } |
| 169 if (start < 0 || start > this.length) { |
| 170 throw new RangeError( |
| 171 "startIndex out of range: $startIndex (length: $length)"); |
| 172 } |
| 173 if (endIndex == null) { |
| 174 end = this.length; |
| 175 } else if (endIndex is! int) { |
| 176 throw new ArgumentError("endIndex is not int"); |
| 177 } else if (endIndex >= 0) { |
| 178 end = endIndex; |
| 179 } else { |
| 180 end = this.length + endIndex; |
| 181 } |
| 182 if (end < 0 || end > this.length) { |
| 183 throw new RangeError( |
| 184 "endIndex out of range: $endIndex (length: $length)"); |
| 185 } |
| 186 if (end < start) { |
| 187 throw new ArgumentError( |
| 188 "End before start: $endIndex < $startIndex (length: $length)"); |
| 189 } |
| 190 return _substringUnchecked(start, end); |
| 191 } |
| 192 |
158 String _substringUnchecked(int startIndex, int endIndex) { | 193 String _substringUnchecked(int startIndex, int endIndex) { |
159 assert(endIndex != null); | 194 assert(endIndex != null); |
160 assert((startIndex >= 0) && (startIndex <= this.length)); | 195 assert((startIndex >= 0) && (startIndex <= this.length)); |
161 assert((endIndex >= 0) && (endIndex <= this.length)); | 196 assert((endIndex >= 0) && (endIndex <= this.length)); |
162 assert(startIndex <= endIndex); | 197 assert(startIndex <= endIndex); |
163 | 198 |
164 if (startIndex == endIndex) { | 199 if (startIndex == endIndex) { |
165 return ""; | 200 return ""; |
166 } | 201 } |
167 if ((startIndex + 1) == endIndex) { | 202 if ((startIndex + 1) == endIndex) { |
(...skipping 29 matching lines...) Expand all Loading... |
197 return this; | 232 return this; |
198 } else { | 233 } else { |
199 return _substringUnchecked(first, last + 1); | 234 return _substringUnchecked(first, last + 1); |
200 } | 235 } |
201 } | 236 } |
202 | 237 |
203 bool contains(Pattern pattern, [int startIndex = 0]) { | 238 bool contains(Pattern pattern, [int startIndex = 0]) { |
204 if (pattern is String) { | 239 if (pattern is String) { |
205 return indexOf(pattern, startIndex) >= 0; | 240 return indexOf(pattern, startIndex) >= 0; |
206 } | 241 } |
207 return pattern.allMatches(this.substring(startIndex)).iterator().hasNext; | 242 return pattern.allMatches(this.substring(startIndex)).iterator.moveNext(); |
208 } | 243 } |
209 | 244 |
210 String replaceFirst(Pattern pattern, String replacement) { | 245 String replaceFirst(Pattern pattern, String replacement) { |
211 if (pattern is! Pattern) { | 246 if (pattern is! Pattern) { |
212 throw new ArgumentError("${pattern} is not a Pattern"); | 247 throw new ArgumentError("${pattern} is not a Pattern"); |
213 } | 248 } |
214 if (replacement is! String) { | 249 if (replacement is! String) { |
215 throw new ArgumentError("${replacement} is not a String"); | 250 throw new ArgumentError("${replacement} is not a String"); |
216 } | 251 } |
217 StringBuffer buffer = new StringBuffer(); | 252 StringBuffer buffer = new StringBuffer(); |
218 int startIndex = 0; | 253 int startIndex = 0; |
219 Iterator iterator = pattern.allMatches(this).iterator(); | 254 Iterator iterator = pattern.allMatches(this).iterator; |
220 if (iterator.hasNext) { | 255 if (iterator.moveNext()) { |
221 Match match = iterator.next(); | 256 Match match = iterator.current; |
222 buffer..add(this.substring(startIndex, match.start)) | 257 buffer..add(this.substring(startIndex, match.start)) |
223 ..add(replacement); | 258 ..add(replacement); |
224 startIndex = match.end; | 259 startIndex = match.end; |
225 } | 260 } |
226 return (buffer..add(this.substring(startIndex))).toString(); | 261 return (buffer..add(this.substring(startIndex))).toString(); |
227 } | 262 } |
228 | 263 |
229 String replaceAll(Pattern pattern, String replacement) { | 264 String replaceAll(Pattern pattern, String replacement) { |
230 if (pattern is! Pattern) { | 265 if (pattern is! Pattern) { |
231 throw new ArgumentError("${pattern} is not a Pattern"); | 266 throw new ArgumentError("${pattern} is not a Pattern"); |
232 } | 267 } |
233 if (replacement is! String) { | 268 if (replacement is! String) { |
234 throw new ArgumentError("${replacement} is not a String"); | 269 throw new ArgumentError( |
| 270 "${replacement} is not a String or Match->String function"); |
235 } | 271 } |
236 StringBuffer buffer = new StringBuffer(); | 272 StringBuffer buffer = new StringBuffer(); |
237 int startIndex = 0; | 273 int startIndex = 0; |
238 for (Match match in pattern.allMatches(this)) { | 274 for (Match match in pattern.allMatches(this)) { |
239 buffer..add(this.substring(startIndex, match.start)) | 275 buffer..add(this.substring(startIndex, match.start)) |
240 ..add(replacement); | 276 ..add(replacement); |
241 startIndex = match.end; | 277 startIndex = match.end; |
242 } | 278 } |
243 return (buffer..add(this.substring(startIndex))).toString(); | 279 return (buffer..add(this.substring(startIndex))).toString(); |
244 } | 280 } |
245 | 281 |
| 282 String replaceAllMapped(Pattern pattern, String replace(Match match)) { |
| 283 return splitMapJoin(pattern, onMatch: replace); |
| 284 } |
| 285 |
| 286 static String _matchString(Match match) => match[0]; |
| 287 static String _stringIdentity(String string) => string; |
| 288 |
| 289 String _splitMapJoinEmptyString(String onMatch(Match match), |
| 290 String onNonMatch(String nonMatch)) { |
| 291 // Pattern is the empty string. |
| 292 StringBuffer buffer = new StringBuffer(); |
| 293 int length = this.length; |
| 294 int i = 0; |
| 295 buffer.add(onNonMatch("")); |
| 296 while (i < length) { |
| 297 buffer.add(onMatch(new _StringMatch(i, this, ""))); |
| 298 // Special case to avoid splitting a surrogate pair. |
| 299 int code = this.charCodeAt(i); |
| 300 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { |
| 301 // Leading surrogate; |
| 302 code = this.charCodeAt(i + 1); |
| 303 if ((code & ~0x3FF) == 0xDC00) { |
| 304 // Matching trailing surrogate. |
| 305 buffer.add(onNonMatch(this.substring(i, i + 2))); |
| 306 i += 2; |
| 307 continue; |
| 308 } |
| 309 } |
| 310 buffer.add(onNonMatch(this[i])); |
| 311 i++; |
| 312 } |
| 313 buffer.add(onMatch(new _StringMatch(i, this, ""))); |
| 314 buffer.add(onNonMatch("")); |
| 315 return buffer.toString(); |
| 316 } |
| 317 |
| 318 String splitMapJoin(Pattern pattern, |
| 319 {String onMatch(Match match), |
| 320 String onNonMatch(String nonMatch)}) { |
| 321 if (pattern is! Pattern) { |
| 322 throw new ArgumentError("${pattern} is not a Pattern"); |
| 323 } |
| 324 if (onMatch == null) onMatch = _matchString; |
| 325 if (onNonMatch == null) onNonMatch = _stringIdentity; |
| 326 if (pattern is String) { |
| 327 String stringPattern = pattern; |
| 328 if (stringPattern.isEmpty) { |
| 329 return _splitMapJoinEmptyString(onMatch, onNonMatch); |
| 330 } |
| 331 } |
| 332 StringBuffer buffer = new StringBuffer(); |
| 333 int startIndex = 0; |
| 334 for (Match match in pattern.allMatches(this)) { |
| 335 buffer.add(onNonMatch(this.substring(startIndex, match.start))); |
| 336 buffer.add(onMatch(match).toString()); |
| 337 startIndex = match.end; |
| 338 } |
| 339 buffer.add(onNonMatch(this.substring(startIndex))); |
| 340 return buffer.toString(); |
| 341 } |
| 342 |
| 343 |
246 /** | 344 /** |
247 * Convert all objects in [values] to strings and concat them | 345 * Convert all objects in [values] to strings and concat them |
248 * into a result string. | 346 * into a result string. |
249 */ | 347 */ |
250 static String _interpolate(List values) { | 348 static String _interpolate(List values) { |
251 int numValues = values.length; | 349 int numValues = values.length; |
252 var stringList = new List(numValues); | 350 var stringList = new List.fixedLength(numValues); |
253 for (int i = 0; i < numValues; i++) { | 351 for (int i = 0; i < numValues; i++) { |
254 stringList[i] = values[i].toString(); | 352 stringList[i] = values[i].toString(); |
255 } | 353 } |
256 return _concatAll(stringList); | 354 return _concatAll(stringList); |
257 } | 355 } |
258 | 356 |
259 Iterable<Match> allMatches(String str) { | 357 Iterable<Match> allMatches(String str) { |
260 List<Match> result = new List<Match>(); | 358 List<Match> result = new List<Match>(); |
261 int length = str.length; | 359 int length = str.length; |
262 int patternLength = this.length; | 360 int patternLength = this.length; |
(...skipping 14 matching lines...) Expand all Loading... |
277 } | 375 } |
278 } | 376 } |
279 return result; | 377 return result; |
280 } | 378 } |
281 | 379 |
282 List<String> split(Pattern pattern) { | 380 List<String> split(Pattern pattern) { |
283 if ((pattern is String) && pattern.isEmpty) { | 381 if ((pattern is String) && pattern.isEmpty) { |
284 return splitChars(); | 382 return splitChars(); |
285 } | 383 } |
286 int length = this.length; | 384 int length = this.length; |
287 Iterator iterator = pattern.allMatches(this).iterator(); | 385 Iterator iterator = pattern.allMatches(this).iterator; |
288 if (length == 0 && iterator.hasNext) { | 386 if (length == 0 && iterator.moveNext()) { |
289 // A matched empty string input returns the empty list. | 387 // A matched empty string input returns the empty list. |
290 return <String>[]; | 388 return <String>[]; |
291 } | 389 } |
292 List<String> result = new List<String>(); | 390 List<String> result = new List<String>(); |
293 int startIndex = 0; | 391 int startIndex = 0; |
294 int previousIndex = 0; | 392 int previousIndex = 0; |
295 while (true) { | 393 while (true) { |
296 if (startIndex == length || !iterator.hasNext) { | 394 if (startIndex == length || !iterator.moveNext()) { |
297 result.add(this._substringUnchecked(previousIndex, length)); | 395 result.add(this._substringUnchecked(previousIndex, length)); |
298 break; | 396 break; |
299 } | 397 } |
300 Match match = iterator.next(); | 398 Match match = iterator.current; |
301 if (match.start == length) { | 399 if (match.start == length) { |
302 result.add(this._substringUnchecked(previousIndex, length)); | 400 result.add(this._substringUnchecked(previousIndex, length)); |
303 break; | 401 break; |
304 } | 402 } |
305 int endIndex = match.end; | 403 int endIndex = match.end; |
306 if (startIndex == endIndex && endIndex == previousIndex) { | 404 if (startIndex == endIndex && endIndex == previousIndex) { |
307 ++startIndex; // empty match, advance and restart | 405 ++startIndex; // empty match, advance and restart |
308 continue; | 406 continue; |
309 } | 407 } |
310 result.add(this._substringUnchecked(previousIndex, match.start)); | 408 result.add(this._substringUnchecked(previousIndex, match.start)); |
311 startIndex = previousIndex = endIndex; | 409 startIndex = previousIndex = endIndex; |
312 } | 410 } |
313 return result; | 411 return result; |
314 } | 412 } |
315 | 413 |
316 List<String> splitChars() { | 414 List<String> splitChars() { |
317 int len = this.length; | 415 int len = this.length; |
318 final result = new List<String>(len); | 416 final result = new List<String>.fixedLength(len); |
319 for (int i = 0; i < len; i++) { | 417 for (int i = 0; i < len; i++) { |
320 result[i] = this[i]; | 418 result[i] = this[i]; |
321 } | 419 } |
322 return result; | 420 return result; |
323 } | 421 } |
324 | 422 |
325 List<int> get charCodes { | 423 List<int> get charCodes { |
326 int len = this.length; | 424 int len = this.length; |
327 final result = new List<int>(len); | 425 final result = new List<int>.fixedLength(len); |
328 for (int i = 0; i < len; i++) { | 426 for (int i = 0; i < len; i++) { |
329 result[i] = this.charCodeAt(i); | 427 result[i] = this.charCodeAt(i); |
330 } | 428 } |
331 return result; | 429 return result; |
332 } | 430 } |
333 | 431 |
334 String toUpperCase() native "String_toUpperCase"; | 432 String toUpperCase() native "String_toUpperCase"; |
335 | 433 |
336 String toLowerCase() native "String_toLowerCase"; | 434 String toLowerCase() native "String_toLowerCase"; |
337 | 435 |
338 // Implementations of Strings methods follow below. | 436 // Implementations of Strings methods follow below. |
339 static String join(List<String> strings, String separator) { | 437 static String join(Iterable<String> strings, String separator) { |
340 final int length = strings.length; | 438 bool first = true; |
341 if (length == 0) { | 439 List<String> stringsList = <String>[]; |
342 return ""; | 440 for (String string in strings) { |
343 } | 441 if (first) { |
| 442 first = false; |
| 443 } else { |
| 444 stringsList.add(separator); |
| 445 } |
344 | 446 |
345 List stringsList = strings; | 447 if (string is! String) { |
346 if (separator.length != 0) { | 448 throw new ArgumentError(Error.safeToString(string)); |
347 stringsList = new List(2 * length - 1); | |
348 stringsList[0] = strings[0]; | |
349 int j = 1; | |
350 for (int i = 1; i < length; i++) { | |
351 stringsList[j++] = separator; | |
352 stringsList[j++] = strings[i]; | |
353 } | 449 } |
| 450 stringsList.add(string); |
354 } | 451 } |
355 return concatAll(stringsList); | 452 return concatAll(stringsList); |
356 } | 453 } |
357 | 454 |
358 static String concatAll(List<String> strings) { | 455 static String concatAll(Iterable<String> strings) { |
359 _ObjectArray stringsArray; | 456 _ObjectArray stringsArray; |
360 if (strings is _ObjectArray) { | 457 if (strings is _ObjectArray) { |
361 stringsArray = strings; | 458 stringsArray = strings; |
| 459 for (int i = 0; i < strings.length; i++) { |
| 460 if (strings[i] is! String) throw new ArgumentError(strings[i]); |
| 461 } |
362 } else { | 462 } else { |
363 int len = strings.length; | 463 int len = strings.length; |
364 stringsArray = new _ObjectArray(len); | 464 stringsArray = new _ObjectArray(len); |
365 for (int i = 0; i < len; i++) { | 465 int i = 0; |
366 stringsArray[i] = strings[i]; | 466 for (String string in strings) { |
| 467 if (string is! String) throw new ArgumentError(string); |
| 468 stringsArray[i++] = string; |
367 } | 469 } |
368 } | 470 } |
369 return _concatAll(stringsArray); | 471 return _concatAll(stringsArray); |
370 } | 472 } |
371 | 473 |
372 static String _concatAll(List<String> strings) | 474 static String _concatAll(List<String> strings) |
373 native "Strings_concatAll"; | 475 native "Strings_concatAll"; |
374 } | 476 } |
375 | 477 |
376 | 478 |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
510 for (int g in groups) { | 612 for (int g in groups) { |
511 result.add(group(g)); | 613 result.add(group(g)); |
512 } | 614 } |
513 return result; | 615 return result; |
514 } | 616 } |
515 | 617 |
516 final int start; | 618 final int start; |
517 final String str; | 619 final String str; |
518 final String pattern; | 620 final String pattern; |
519 } | 621 } |
OLD | NEW |