OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of _js_helper; | |
6 | |
7 stringIndexOfStringUnchecked(receiver, other, startIndex) { | |
8 return JS('int', '#.indexOf(#, #)', receiver, other, startIndex); | |
9 } | |
10 | |
11 substring1Unchecked(receiver, startIndex) { | |
12 return JS('String', '#.substring(#)', receiver, startIndex); | |
13 } | |
14 | |
15 substring2Unchecked(receiver, startIndex, endIndex) { | |
16 return JS('String', '#.substring(#, #)', receiver, startIndex, endIndex); | |
17 } | |
18 | |
19 stringContainsStringUnchecked(receiver, other, startIndex) { | |
20 return stringIndexOfStringUnchecked(receiver, other, startIndex) >= 0; | |
21 } | |
22 | |
23 class StringMatch implements Match { | |
24 const StringMatch(int this.start, | |
25 String this.input, | |
26 String this.pattern); | |
27 | |
28 int get end => start + pattern.length; | |
29 String operator[](int g) => group(g); | |
30 int get groupCount => 0; | |
31 | |
32 String group(int group_) { | |
33 if (group_ != 0) { | |
34 throw new RangeError.value(group_); | |
35 } | |
36 return pattern; | |
37 } | |
38 | |
39 List<String> groups(List<int> groups_) { | |
40 List<String> result = new List<String>(); | |
41 for (int g in groups_) { | |
42 result.add(group(g)); | |
43 } | |
44 return result; | |
45 } | |
46 | |
47 final int start; | |
48 final String input; | |
49 final String pattern; | |
50 } | |
51 | |
52 List<Match> allMatchesInStringUnchecked(String pattern, String string, | |
53 int startIndex) { | |
54 // Copied from StringBase.allMatches in | |
55 // /runtime/lib/string_base.dart | |
56 List<Match> result = new List<Match>(); | |
57 int length = string.length; | |
58 int patternLength = pattern.length; | |
59 while (true) { | |
60 int position = stringIndexOfStringUnchecked(string, pattern, startIndex); | |
61 if (position == -1) { | |
62 break; | |
63 } | |
64 result.add(new StringMatch(position, string, pattern)); | |
65 int endIndex = position + patternLength; | |
66 if (endIndex == length) { | |
67 break; | |
68 } else if (position == endIndex) { | |
69 ++startIndex; // empty match, advance and restart | |
70 } else { | |
71 startIndex = endIndex; | |
72 } | |
73 } | |
74 return result; | |
75 } | |
76 | |
77 stringContainsUnchecked(receiver, other, startIndex) { | |
78 if (other is String) { | |
79 return stringContainsStringUnchecked(receiver, other, startIndex); | |
80 } else if (other is JSSyntaxRegExp) { | |
81 return other.hasMatch(receiver.substring(startIndex)); | |
82 } else { | |
83 var substr = receiver.substring(startIndex); | |
84 return other.allMatches(substr).isNotEmpty; | |
85 } | |
86 } | |
87 | |
88 stringReplaceJS(receiver, replacer, replacement) { | |
89 // The JavaScript String.replace method recognizes replacement | |
90 // patterns in the replacement string. Dart does not have that | |
91 // behavior. | |
92 replacement = JS('String', r'#.replace(/\$/g, "$$$$")', replacement); | |
93 return JS('String', r'#.replace(#, #)', receiver, replacer, replacement); | |
94 } | |
95 | |
96 stringReplaceFirstRE(receiver, regexp, replacement, startIndex) { | |
97 var match = regexp._execGlobal(receiver, startIndex); | |
98 if (match == null) return receiver; | |
99 var start = match.start; | |
100 var end = match.end; | |
101 return stringReplaceRangeUnchecked(receiver, start, end, replacement); | |
102 } | |
103 | |
104 const String ESCAPE_REGEXP = r'[[\]{}()*+?.\\^$|]'; | |
105 | |
106 stringReplaceAllUnchecked(receiver, pattern, replacement) { | |
107 checkString(replacement); | |
108 if (pattern is String) { | |
109 if (pattern == "") { | |
110 if (receiver == "") { | |
111 return replacement; | |
112 } else { | |
113 StringBuffer result = new StringBuffer(); | |
114 int length = receiver.length; | |
115 result.write(replacement); | |
116 for (int i = 0; i < length; i++) { | |
117 result.write(receiver[i]); | |
118 result.write(replacement); | |
119 } | |
120 return result.toString(); | |
121 } | |
122 } else { | |
123 var quoter = JS('', "new RegExp(#, 'g')", ESCAPE_REGEXP); | |
124 var quoted = JS('String', r'#.replace(#, "\\$&")', pattern, quoter); | |
125 var replacer = JS('', "new RegExp(#, 'g')", quoted); | |
126 return stringReplaceJS(receiver, replacer, replacement); | |
127 } | |
128 } else if (pattern is JSSyntaxRegExp) { | |
129 var re = regExpGetGlobalNative(pattern); | |
130 return stringReplaceJS(receiver, re, replacement); | |
131 } else { | |
132 checkNull(pattern); | |
133 // TODO(floitsch): implement generic String.replace (with patterns). | |
134 throw "String.replaceAll(Pattern) UNIMPLEMENTED"; | |
135 } | |
136 } | |
137 | |
138 String _matchString(Match match) => match[0]; | |
139 String _stringIdentity(String string) => string; | |
140 | |
141 stringReplaceAllFuncUnchecked(receiver, pattern, onMatch, onNonMatch) { | |
142 if (onMatch == null) onMatch = _matchString; | |
143 if (onNonMatch == null) onNonMatch = _stringIdentity; | |
144 if (pattern is String) { | |
145 return stringReplaceAllStringFuncUnchecked(receiver, pattern, | |
146 onMatch, onNonMatch); | |
147 } | |
148 // Placing the Pattern test here is indistingishable from placing it at the | |
149 // top of the method but it saves an extra check on the `pattern is String` | |
150 // path. | |
151 if (pattern is! Pattern) { | |
152 throw new ArgumentError.value(pattern, 'pattern', 'is not a Pattern'); | |
153 } | |
154 StringBuffer buffer = new StringBuffer(); | |
155 int startIndex = 0; | |
156 for (Match match in pattern.allMatches(receiver)) { | |
157 buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); | |
158 buffer.write(onMatch(match)); | |
159 startIndex = match.end; | |
160 } | |
161 buffer.write(onNonMatch(receiver.substring(startIndex))); | |
162 return buffer.toString(); | |
163 } | |
164 | |
165 stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch) { | |
166 // Pattern is the empty string. | |
167 StringBuffer buffer = new StringBuffer(); | |
168 int length = receiver.length; | |
169 int i = 0; | |
170 buffer.write(onNonMatch("")); | |
171 while (i < length) { | |
172 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | |
173 // Special case to avoid splitting a surrogate pair. | |
174 int code = receiver.codeUnitAt(i); | |
175 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { | |
176 // Leading surrogate; | |
177 code = receiver.codeUnitAt(i + 1); | |
178 if ((code & ~0x3FF) == 0xDC00) { | |
179 // Matching trailing surrogate. | |
180 buffer.write(onNonMatch(receiver.substring(i, i + 2))); | |
181 i += 2; | |
182 continue; | |
183 } | |
184 } | |
185 buffer.write(onNonMatch(receiver[i])); | |
186 i++; | |
187 } | |
188 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | |
189 buffer.write(onNonMatch("")); | |
190 return buffer.toString(); | |
191 } | |
192 | |
193 stringReplaceAllStringFuncUnchecked(receiver, pattern, onMatch, onNonMatch) { | |
194 int patternLength = pattern.length; | |
195 if (patternLength == 0) { | |
196 return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); | |
197 } | |
198 int length = receiver.length; | |
199 StringBuffer buffer = new StringBuffer(); | |
200 int startIndex = 0; | |
201 while (startIndex < length) { | |
202 int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | |
203 if (position == -1) { | |
204 break; | |
205 } | |
206 buffer.write(onNonMatch(receiver.substring(startIndex, position))); | |
207 buffer.write(onMatch(new StringMatch(position, receiver, pattern))); | |
208 startIndex = position + patternLength; | |
209 } | |
210 buffer.write(onNonMatch(receiver.substring(startIndex))); | |
211 return buffer.toString(); | |
212 } | |
213 | |
214 | |
215 stringReplaceFirstUnchecked(receiver, pattern, replacement, int startIndex) { | |
216 if (pattern is String) { | |
217 int index = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | |
218 if (index < 0) return receiver; | |
219 int end = index + pattern.length; | |
220 return stringReplaceRangeUnchecked(receiver, index, end, replacement); | |
221 } | |
222 if (pattern is JSSyntaxRegExp) { | |
223 return startIndex == 0 | |
224 ? stringReplaceJS(receiver, regExpGetNative(pattern), replacement) | |
225 : stringReplaceFirstRE(receiver, pattern, replacement, startIndex); | |
226 } | |
227 checkNull(pattern); | |
228 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | |
229 if (!matches.moveNext()) return receiver; | |
230 Match match = matches.current; | |
231 return receiver.replaceRange(match.start, match.end, replacement); | |
232 } | |
233 | |
234 stringReplaceFirstMappedUnchecked(receiver, pattern, replace, | |
235 int startIndex) { | |
236 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | |
237 if (!matches.moveNext()) return receiver; | |
238 Match match = matches.current; | |
239 String replacement = "${replace(match)}"; | |
240 return receiver.replaceRange(match.start, match.end, replacement); | |
241 } | |
242 | |
243 stringJoinUnchecked(array, separator) { | |
244 return JS('String', r'#.join(#)', array, separator); | |
245 } | |
246 | |
247 String stringReplaceRangeUnchecked(String receiver, | |
248 int start, int end, String replacement) { | |
249 var prefix = JS('String', '#.substring(0, #)', receiver, start); | |
250 var suffix = JS('String', '#.substring(#)', receiver, end); | |
251 return "$prefix$replacement$suffix"; | |
252 } | |
OLD | NEW |