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._js_helper; | 5 part of dart._js_helper; |
6 | 6 |
| 7 @notNull |
7 int stringIndexOfStringUnchecked(receiver, other, startIndex) { | 8 int stringIndexOfStringUnchecked(receiver, other, startIndex) { |
8 return JS('int', '#.indexOf(#, #)', receiver, other, startIndex); | 9 return JS('int', '#.indexOf(#, #)', receiver, other, startIndex); |
9 } | 10 } |
10 | 11 |
| 12 @notNull |
11 String substring1Unchecked(receiver, startIndex) { | 13 String substring1Unchecked(receiver, startIndex) { |
12 return JS('String', '#.substring(#)', receiver, startIndex); | 14 return JS('String', '#.substring(#)', receiver, startIndex); |
13 } | 15 } |
14 | 16 |
| 17 @notNull |
15 String substring2Unchecked(receiver, startIndex, endIndex) { | 18 String substring2Unchecked(receiver, startIndex, endIndex) { |
16 return JS('String', '#.substring(#, #)', receiver, startIndex, endIndex); | 19 return JS('String', '#.substring(#, #)', receiver, startIndex, endIndex); |
17 } | 20 } |
18 | 21 |
| 22 @notNull |
19 bool stringContainsStringUnchecked(receiver, other, startIndex) { | 23 bool stringContainsStringUnchecked(receiver, other, startIndex) { |
20 return stringIndexOfStringUnchecked(receiver, other, startIndex) >= 0; | 24 return stringIndexOfStringUnchecked(receiver, other, startIndex) >= 0; |
21 } | 25 } |
22 | 26 |
23 class StringMatch implements Match { | 27 class StringMatch implements Match { |
24 const StringMatch(int this.start, String this.input, String this.pattern); | 28 const StringMatch(int this.start, String this.input, String this.pattern); |
25 | 29 |
26 int get end => start + pattern.length; | 30 int get end => start + pattern.length; |
27 String operator [](int g) => group(g); | 31 String operator [](int g) => group(g); |
28 int get groupCount => 0; | 32 int get groupCount => 0; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 _current = new StringMatch(index, _input, _pattern); | 98 _current = new StringMatch(index, _input, _pattern); |
95 // Empty match, don't start at same location again. | 99 // Empty match, don't start at same location again. |
96 if (end == _index) end++; | 100 if (end == _index) end++; |
97 _index = end; | 101 _index = end; |
98 return true; | 102 return true; |
99 } | 103 } |
100 | 104 |
101 Match get current => _current; | 105 Match get current => _current; |
102 } | 106 } |
103 | 107 |
104 bool stringContainsUnchecked(String receiver, other, int startIndex) { | 108 @notNull |
| 109 bool stringContainsUnchecked( |
| 110 @notNull String receiver, @notNull other, int startIndex) { |
105 if (other is String) { | 111 if (other is String) { |
106 return stringContainsStringUnchecked(receiver, other, startIndex); | 112 return stringContainsStringUnchecked(receiver, other, startIndex); |
107 } else if (other is JSSyntaxRegExp) { | 113 } else if (other is JSSyntaxRegExp) { |
108 return other.hasMatch(receiver.substring(startIndex)); | 114 return other.hasMatch(receiver.substring(startIndex)); |
109 } else { | 115 } else { |
110 var substr = receiver.substring(startIndex); | 116 var substr = receiver.substring(startIndex); |
111 return other.allMatches(substr).isNotEmpty; | 117 return other.allMatches(substr).isNotEmpty; |
112 } | 118 } |
113 } | 119 } |
114 | 120 |
| 121 @notNull |
115 String stringReplaceJS(receiver, replacer, replacement) { | 122 String stringReplaceJS(receiver, replacer, replacement) { |
116 // The JavaScript String.replace method recognizes replacement | 123 // The JavaScript String.replace method recognizes replacement |
117 // patterns in the replacement string. Dart does not have that | 124 // patterns in the replacement string. Dart does not have that |
118 // behavior. | 125 // behavior. |
119 replacement = JS('String', r'#.replace(/\$/g, "$$$$")', replacement); | 126 replacement = JS('String', r'#.replace(/\$/g, "$$$$")', replacement); |
120 return JS('String', r'#.replace(#, #)', receiver, replacer, replacement); | 127 return JS('String', r'#.replace(#, #)', receiver, replacer, replacement); |
121 } | 128 } |
122 | 129 |
123 String stringReplaceFirstRE(String receiver, JSSyntaxRegExp regexp, | 130 @notNull |
| 131 String stringReplaceFirstRE(@notNull String receiver, JSSyntaxRegExp regexp, |
124 String replacement, int startIndex) { | 132 String replacement, int startIndex) { |
125 var match = regexp._execGlobal(receiver, startIndex); | 133 var match = regexp._execGlobal(receiver, startIndex); |
126 if (match == null) return receiver; | 134 if (match == null) return receiver; |
127 var start = match.start; | 135 var start = match.start; |
128 var end = match.end; | 136 var end = match.end; |
129 return stringReplaceRangeUnchecked(receiver, start, end, replacement); | 137 return stringReplaceRangeUnchecked(receiver, start, end, replacement); |
130 } | 138 } |
131 | 139 |
132 /// Returns a string for a RegExp pattern that matches [string]. This is done by | 140 /// Returns a string for a RegExp pattern that matches [string]. This is done by |
133 /// escaping all RegExp metacharacters. | 141 /// escaping all RegExp metacharacters. |
| 142 @notNull |
134 String quoteStringForRegExp(string) { | 143 String quoteStringForRegExp(string) { |
135 return JS('String', r'#.replace(/[[\]{}()*+?.\\^$|]/g, "\\$&")', string); | 144 return JS('String', r'#.replace(/[[\]{}()*+?.\\^$|]/g, "\\$&")', string); |
136 } | 145 } |
137 | 146 |
138 String stringReplaceAllUnchecked( | 147 @notNull |
139 String receiver, Pattern pattern, String replacement) { | 148 String stringReplaceAllUnchecked(@notNull String receiver, |
140 checkString(replacement); | 149 @nullCheck Pattern pattern, @nullCheck String replacement) { |
141 if (pattern is String) { | 150 if (pattern is String) { |
142 if (pattern == "") { | 151 if (pattern == "") { |
143 if (receiver == "") { | 152 if (receiver == "") { |
144 return replacement; | 153 return replacement; |
145 } else { | 154 } else { |
146 StringBuffer result = new StringBuffer(); | 155 StringBuffer result = new StringBuffer(); |
147 int length = receiver.length; | 156 int length = receiver.length; |
148 result.write(replacement); | 157 result.write(replacement); |
149 for (int i = 0; i < length; i++) { | 158 for (int i = 0; i < length; i++) { |
150 result.write(receiver[i]); | 159 result.write(receiver[i]); |
151 result.write(replacement); | 160 result.write(replacement); |
152 } | 161 } |
153 return result.toString(); | 162 return result.toString(); |
154 } | 163 } |
155 } else { | 164 } else { |
156 var quoted = quoteStringForRegExp(pattern); | 165 var quoted = quoteStringForRegExp(pattern); |
157 var replacer = JS('', "new RegExp(#, 'g')", quoted); | 166 var replacer = JS('', "new RegExp(#, 'g')", quoted); |
158 return stringReplaceJS(receiver, replacer, replacement); | 167 return stringReplaceJS(receiver, replacer, replacement); |
159 } | 168 } |
160 } else if (pattern is JSSyntaxRegExp) { | 169 } else if (pattern is JSSyntaxRegExp) { |
161 var re = regExpGetGlobalNative(pattern); | 170 var re = regExpGetGlobalNative(pattern); |
162 return stringReplaceJS(receiver, re, replacement); | 171 return stringReplaceJS(receiver, re, replacement); |
163 } else { | 172 } else { |
164 checkNull(pattern); | |
165 // TODO(floitsch): implement generic String.replace (with patterns). | 173 // TODO(floitsch): implement generic String.replace (with patterns). |
166 throw "String.replaceAll(Pattern) UNIMPLEMENTED"; | 174 throw "String.replaceAll(Pattern) UNIMPLEMENTED"; |
167 } | 175 } |
168 } | 176 } |
169 | 177 |
170 String _matchString(Match match) => match[0]; | 178 String _matchString(Match match) => match[0]; |
171 String _stringIdentity(String string) => string; | 179 String _stringIdentity(String string) => string; |
172 | 180 |
173 String stringReplaceAllFuncUnchecked(String receiver, Pattern pattern, | 181 @notNull |
174 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 182 String stringReplaceAllFuncUnchecked( |
| 183 String receiver, |
| 184 @nullCheck Pattern pattern, |
| 185 String onMatch(Match match), |
| 186 String onNonMatch(String nonMatch)) { |
175 if (onMatch == null) onMatch = _matchString; | 187 if (onMatch == null) onMatch = _matchString; |
176 if (onNonMatch == null) onNonMatch = _stringIdentity; | 188 if (onNonMatch == null) onNonMatch = _stringIdentity; |
177 if (pattern is String) { | 189 if (pattern is String) { |
178 return stringReplaceAllStringFuncUnchecked( | 190 return stringReplaceAllStringFuncUnchecked( |
179 receiver, pattern, onMatch, onNonMatch); | 191 receiver, pattern, onMatch, onNonMatch); |
180 } | 192 } |
181 // Placing the Pattern test here is indistinguishable from placing it at the | |
182 // top of the method but it saves an extra check on the `pattern is String` | |
183 // path. | |
184 if (pattern is! Pattern) { | |
185 throw new ArgumentError.value(pattern, 'pattern', 'is not a Pattern'); | |
186 } | |
187 StringBuffer buffer = new StringBuffer(); | 193 StringBuffer buffer = new StringBuffer(); |
188 int startIndex = 0; | 194 int startIndex = 0; |
189 for (Match match in pattern.allMatches(receiver)) { | 195 for (Match match in pattern.allMatches(receiver)) { |
190 buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); | 196 buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); |
191 buffer.write(onMatch(match)); | 197 buffer.write(onMatch(match)); |
192 startIndex = match.end; | 198 startIndex = match.end; |
193 } | 199 } |
194 buffer.write(onNonMatch(receiver.substring(startIndex))); | 200 buffer.write(onNonMatch(receiver.substring(startIndex))); |
195 return buffer.toString(); | 201 return buffer.toString(); |
196 } | 202 } |
197 | 203 |
| 204 @notNull |
198 String stringReplaceAllEmptyFuncUnchecked(String receiver, | 205 String stringReplaceAllEmptyFuncUnchecked(String receiver, |
199 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 206 String onMatch(Match match), String onNonMatch(String nonMatch)) { |
200 // Pattern is the empty string. | 207 // Pattern is the empty string. |
201 StringBuffer buffer = new StringBuffer(); | 208 StringBuffer buffer = new StringBuffer(); |
202 int length = receiver.length; | 209 int length = receiver.length; |
203 int i = 0; | 210 int i = 0; |
204 buffer.write(onNonMatch("")); | 211 buffer.write(onNonMatch("")); |
205 while (i < length) { | 212 while (i < length) { |
206 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | 213 buffer.write(onMatch(new StringMatch(i, receiver, ""))); |
207 // Special case to avoid splitting a surrogate pair. | 214 // Special case to avoid splitting a surrogate pair. |
208 int code = receiver.codeUnitAt(i); | 215 int code = receiver.codeUnitAt(i); |
209 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { | 216 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { |
210 // Leading surrogate; | 217 // Leading surrogate; |
211 code = receiver.codeUnitAt(i + 1); | 218 code = receiver.codeUnitAt(i + 1); |
212 if ((code & ~0x3FF) == 0xDC00) { | 219 if ((code & ~0x3FF) == 0xDC00) { |
213 // Matching trailing surrogate. | 220 // Matching trailing surrogate. |
214 buffer.write(onNonMatch(receiver.substring(i, i + 2))); | 221 buffer.write(onNonMatch(receiver.substring(i, i + 2))); |
215 i += 2; | 222 i += 2; |
216 continue; | 223 continue; |
217 } | 224 } |
218 } | 225 } |
219 buffer.write(onNonMatch(receiver[i])); | 226 buffer.write(onNonMatch(receiver[i])); |
220 i++; | 227 i++; |
221 } | 228 } |
222 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | 229 buffer.write(onMatch(new StringMatch(i, receiver, ""))); |
223 buffer.write(onNonMatch("")); | 230 buffer.write(onNonMatch("")); |
224 return buffer.toString(); | 231 return buffer.toString(); |
225 } | 232 } |
226 | 233 |
| 234 @notNull |
227 String stringReplaceAllStringFuncUnchecked(String receiver, String pattern, | 235 String stringReplaceAllStringFuncUnchecked(String receiver, String pattern, |
228 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 236 String onMatch(Match match), String onNonMatch(String nonMatch)) { |
229 int patternLength = pattern.length; | 237 int patternLength = pattern.length; |
230 if (patternLength == 0) { | 238 if (patternLength == 0) { |
231 return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); | 239 return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); |
232 } | 240 } |
233 int length = receiver.length; | 241 int length = receiver.length; |
234 StringBuffer buffer = new StringBuffer(); | 242 StringBuffer buffer = new StringBuffer(); |
235 int startIndex = 0; | 243 int startIndex = 0; |
236 while (startIndex < length) { | 244 while (startIndex < length) { |
237 int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | 245 int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex); |
238 if (position == -1) { | 246 if (position == -1) { |
239 break; | 247 break; |
240 } | 248 } |
241 buffer.write(onNonMatch(receiver.substring(startIndex, position))); | 249 buffer.write(onNonMatch(receiver.substring(startIndex, position))); |
242 buffer.write(onMatch(new StringMatch(position, receiver, pattern))); | 250 buffer.write(onMatch(new StringMatch(position, receiver, pattern))); |
243 startIndex = position + patternLength; | 251 startIndex = position + patternLength; |
244 } | 252 } |
245 buffer.write(onNonMatch(receiver.substring(startIndex))); | 253 buffer.write(onNonMatch(receiver.substring(startIndex))); |
246 return buffer.toString(); | 254 return buffer.toString(); |
247 } | 255 } |
248 | 256 |
249 String stringReplaceFirstUnchecked( | 257 @notNull |
250 String receiver, Pattern pattern, String replacement, int startIndex) { | 258 String stringReplaceFirstUnchecked(@notNull String receiver, |
| 259 @nullCheck Pattern pattern, String replacement, int startIndex) { |
251 if (pattern is String) { | 260 if (pattern is String) { |
252 int index = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | 261 int index = stringIndexOfStringUnchecked(receiver, pattern, startIndex); |
253 if (index < 0) return receiver; | 262 if (index < 0) return receiver; |
254 int end = index + pattern.length; | 263 int end = index + pattern.length; |
255 return stringReplaceRangeUnchecked(receiver, index, end, replacement); | 264 return stringReplaceRangeUnchecked(receiver, index, end, replacement); |
256 } | 265 } |
257 if (pattern is JSSyntaxRegExp) { | 266 if (pattern is JSSyntaxRegExp) { |
258 return startIndex == 0 | 267 return startIndex == 0 |
259 ? stringReplaceJS(receiver, regExpGetNative(pattern), replacement) | 268 ? stringReplaceJS(receiver, regExpGetNative(pattern), replacement) |
260 : stringReplaceFirstRE(receiver, pattern, replacement, startIndex); | 269 : stringReplaceFirstRE(receiver, pattern, replacement, startIndex); |
261 } | 270 } |
262 checkNull(pattern); | |
263 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | 271 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; |
264 if (!matches.moveNext()) return receiver; | 272 if (!matches.moveNext()) return receiver; |
265 Match match = matches.current; | 273 Match match = matches.current; |
266 return receiver.replaceRange(match.start, match.end, replacement); | 274 return receiver.replaceRange(match.start, match.end, replacement); |
267 } | 275 } |
268 | 276 |
| 277 @notNull |
269 String stringReplaceFirstMappedUnchecked(String receiver, Pattern pattern, | 278 String stringReplaceFirstMappedUnchecked(String receiver, Pattern pattern, |
270 String replace(Match current), int startIndex) { | 279 String replace(Match current), int startIndex) { |
271 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | 280 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; |
272 if (!matches.moveNext()) return receiver; | 281 if (!matches.moveNext()) return receiver; |
273 Match match = matches.current; | 282 Match match = matches.current; |
274 String replacement = "${replace(match)}"; | 283 String replacement = "${replace(match)}"; |
275 return receiver.replaceRange(match.start, match.end, replacement); | 284 return receiver.replaceRange(match.start, match.end, replacement); |
276 } | 285 } |
277 | 286 |
| 287 @notNull |
278 String stringJoinUnchecked(array, separator) { | 288 String stringJoinUnchecked(array, separator) { |
279 return JS('String', r'#.join(#)', array, separator); | 289 return JS('String', r'#.join(#)', array, separator); |
280 } | 290 } |
281 | 291 |
| 292 @notNull |
282 String stringReplaceRangeUnchecked( | 293 String stringReplaceRangeUnchecked( |
283 String receiver, int start, int end, String replacement) { | 294 String receiver, int start, int end, String replacement) { |
284 var prefix = JS('String', '#.substring(0, #)', receiver, start); | 295 var prefix = JS('String', '#.substring(0, #)', receiver, start); |
285 var suffix = JS('String', '#.substring(#)', receiver, end); | 296 var suffix = JS('String', '#.substring(#)', receiver, end); |
286 return "$prefix$replacement$suffix"; | 297 return "$prefix$replacement$suffix"; |
287 } | 298 } |
OLD | NEW |