sources/mystring.cpp

branch
protocol5
changeset 150
37db42ad451a
parent 141
d9073c13dc98
parent 149
7643c21d546b
child 159
970d58a01e8b
equal deleted inserted replaced
141:d9073c13dc98 150:37db42ad451a
33 #include "mystring.h" 33 #include "mystring.h"
34 #include "md5.h" 34 #include "md5.h"
35 35
36 BEGIN_ZFC_NAMESPACE 36 BEGIN_ZFC_NAMESPACE
37 37
38 // ------------------------------------------------------------------------------------------------- 38 /*!
39 // 39 * \brief Compares this string with another.
40 * \param other The string to compare with.
41 * \returns -1 if this string is lexicographically less than \c other,
42 * 0 if they are equal, or
43 * 1 if this string is lexicographically greater than \c other.
44 */
40 int String::compare (const String& other) const 45 int String::compare (const String& other) const
41 { 46 {
42 return m_string.compare (other.std_string()); 47 return m_string.compare (other.stdString());
43 } 48 }
44 49
45 // ------------------------------------------------------------------------------------------------- 50 /*!
46 // 51 * \brief Removes all instances of an unwanted character from this string.
47 void String::trim (int n) 52 * \param unwanted Character to remove.
48 { 53 */
49 if (n > 0) 54 void String::strip (char unwanted)
50 m_string = mid (0, length() - n).std_string(); 55 {
51 else 56 for (int pos = 0; (pos = find (unwanted)) != -1;)
52 m_string = mid (n, -1).std_string(); 57 removeAt (pos--);
53 } 58 }
54 59
55 // ------------------------------------------------------------------------------------------------- 60 /*!
56 // 61 * \brief Removes all instances of multiple characters from this string.
57 String String::strip (char unwanted) const 62 * \param unwanted Characters to remove.
58 { 63 */
59 String result (m_string); 64 void String::strip (const List<char>& unwanted)
60 65 {
61 for (int pos = 0; (pos = result.find (unwanted)) != -1;) 66 for (char character : unwanted)
62 result.remove_at (pos--); 67 strip(character);
63 68 }
64 return result; 69
65 } 70 /*!
66 71 * \returns an upper-case version of this string.
67 // ------------------------------------------------------------------------------------------------- 72 */
68 // 73 String String::toUpperCase() const
69 String String::strip (const List<char>& unwanted) const
70 {
71 String result (m_string);
72
73 for (String c : unwanted)
74 for (int pos = 0; (pos = result.find (c)) != -1;)
75 result.remove_at (pos--);
76
77 return result;
78 }
79
80 // -------------------------------------------------------------------------------------------------
81 //
82 String String::to_uppercase() const
83 { 74 {
84 String result (m_string); 75 String result (m_string);
85 76
86 for (char &ch : result) 77 for (char &ch : result)
87 { 78 {
90 } 81 }
91 82
92 return result; 83 return result;
93 } 84 }
94 85
95 // ------------------------------------------------------------------------------------------------- 86 /*!
96 // 87 * \returns a lower-case version of this string.
97 String String::to_lowercase() const 88 */
89 String String::toLowerCase() const
98 { 90 {
99 String result (m_string); 91 String result (m_string);
100 92
101 for (char &ch : result) 93 for (char &ch : result)
102 { 94 {
105 } 97 }
106 98
107 return result; 99 return result;
108 } 100 }
109 101
110 // ------------------------------------------------------------------------------------------------- 102 /*!
111 // 103 * \brief Splits this string using the provided delimeter.
112 StringList String::split (char del) const 104 * \param delimeter Delimeter to use for splitting.
113 { 105 * \returns a string list containing the split strings.
114 String delimstr; 106 */
115 delimstr += del; 107 StringList String::split (char delimeter) const
116 return split (delimstr); 108 {
117 } 109 String delimeterString;
118 110 delimeterString += delimeter;
119 // ------------------------------------------------------------------------------------------------- 111 return split (delimeterString);
120 // 112 }
121 StringList String::split (const String& del) const 113
122 { 114 /*!
123 StringList res; 115 * \brief Splits this string using the provided delimeter.
116 * \param delimeter Delimeter to use for splitting.
117 * \returns a string list containing the split strings.
118 */
119 StringList String::split (const String& delimeter) const
120 {
121 StringList result;
124 int a = 0; 122 int a = 0;
125 int b; 123 int b;
126 124
127 // Find all separators and store the text left to them. 125 // Find all separators and store the text left to them.
128 while ((b = find (del, a)) != -1) 126 while ((b = find (delimeter, a)) != -1)
129 { 127 {
130 String sub = mid (a, b); 128 String sub = mid (a, b);
131 129
132 if (sub.length() > 0) 130 if (sub.length() > 0)
133 res << sub; 131 result << sub;
134 132
135 a = b + del.length(); 133 a = b + delimeter.length();
136 } 134 }
137 135
138 // Add the string at the right of the last separator 136 // Add the string at the right of the last separator
139 if (a < (int) length()) 137 if (a < (int) length())
140 res.append (mid (a, length())); 138 result.append (mid (a, length()));
141 139
142 return res; 140 return result;
143 } 141 }
144 142
145 // ------------------------------------------------------------------------------------------------- 143 /*!
146 // 144 * \brief Replaces all instances of \c text with \c replacement.
147 void String::replace (const char* a, const char* b) 145 * \param text Text to replace away.
148 { 146 * \param replacement Text to replace \c text with.
149 long pos; 147 */
150 148 void String::replace (const char* text, const char* replacement)
151 while ((pos = find (a)) != -1) 149 {
152 m_string = m_string.replace (pos, strlen (a), b); 150 int position;
153 } 151
154 152 while ((position = find (text)) != -1)
155 // ------------------------------------------------------------------------------------------------- 153 m_string = m_string.replace (position, strlen (text), replacement);
156 // 154 }
157 int String::count (char needle) const 155
156 /*!
157 * \param character Character to count.
158 * \returns the amount of \c character found in the string.
159 */
160 int String::count (char character) const
158 { 161 {
159 int result = 0; 162 int result = 0;
160 163
161 for (char ch : *this) 164 for (char ch : *this)
162 { 165 {
163 if (ch == needle) 166 if (ch == character)
164 result++; 167 result++;
165 } 168 }
166 169
167 return result; 170 return result;
168 } 171 }
169 172
170 // ------------------------------------------------------------------------------------------------- 173 /*!
171 // 174 * \param a Starting index of the range.
172 // Returns a substring from [a, b) 175 * \param b Ending index of the range.
173 // 176 * \returns a sub-string containing all characters from \c a to \c b, not including the character at \c b.
174 String String::mid (int a, int b) const 177 */
175 { 178 String String::mid (int rangeBegin, int rangeEnd) const
176 a = max(a, 0); 179 {
177 b = min(b, length()); 180 modifyIndex(rangeBegin);
178 181 modifyIndex(rangeEnd);
179 if (b == -1) 182 rangeBegin = max(rangeBegin, 0);
180 b = length(); 183 rangeEnd = min(rangeEnd, length());
181 184
182 if (b <= a) 185 if (rangeEnd <= rangeBegin)
183 return ""; 186 return "";
184 else 187 else
185 return m_string.substr(a, b - a); 188 return m_string.substr(rangeBegin, rangeEnd - rangeBegin);
186 } 189 }
187 190
188 // ------------------------------------------------------------------------------------------------- 191 /*!
189 // 192 * \param length Amount of characters to return.
193 * \returns the \c length right-most characters of the string.
194 */
190 String String::right(int length) const 195 String String::right(int length) const
191 { 196 {
192 if (length >= this->length()) 197 if (length >= this->length())
193 return *this; 198 return *this;
194 else 199 else
195 return String(chars() + this->length() - length); 200 return String(chars() + this->length() - length);
196 } 201 }
197 202
198 // ------------------------------------------------------------------------------------------------- 203 /*!
199 // 204 * \brief Finds the first instance of a sub-string.
200 int String::word_position (int n) const 205 * \param subString Sub-string to search within this string.
201 { 206 * \param startingPosition Position to start looking for the sub-string from.
202 int count = 0; 207 * \returns the position the first instance of sub-string found, or -1 if not found.
203 208 */
204 for (char ch : *this) 209 int String::find (const char* subString, int startingPosition) const
205 { 210 {
206 if (not isspace(ch) or ++count < n) 211 int position = m_string.find (subString, startingPosition);
207 continue; 212
213 if (position == int (std::string::npos))
214 return -1;
215 else
216 return position;
217 }
218
219 /*!
220 * \brief Finds the first instance of a character.
221 * \param character Character to search within this string.
222 * \param startingPosition Position to start looking for the character from.
223 * \returns the position of the first instance of the provided character found, or -1 if not found.
224 */
225 int String::find (char character, int startingPosition) const
226 {
227 int position = m_string.find (character, startingPosition);
228
229 if (position == int (std::string::npos))
230 return -1;
231 else
232 return position;
233 }
234
235 /*!
236 * \brief Finds the last instance of a sub-string.
237 * \param subString Sub-string to search within this string.
238 * \param startingPosition Position to start looking for the sub-string from.
239 * \returns the position the last instance of sub-string found, or -1 if not found.
240 */
241 int String::findLast (const char* subString, int startingPosition) const
242 {
243 modifyIndex(startingPosition);
244
245 for (; startingPosition > 0; startingPosition--)
246 {
247 if (strncmp (chars() + startingPosition, subString, strlen (subString)) == 0)
248 return startingPosition;
208 } 249 }
209 250
210 return -1; 251 return -1;
211 } 252 }
212 253
213 // ------------------------------------------------------------------------------------------------- 254 /*!
214 // 255 * \brief Converts this string to an integer.
215 int String::find (const char* c, int a) const 256 * \param ok An pointer to a boolean to store whether or not the conversion was successful.
216 { 257 * If \c ok is \c NULL, the success state is not stored.
217 int pos = m_string.find (c, a); 258 * \param base The base to interpret this string with.
218 259 * \returns the resulting integer.
219 if (pos == int (std::string::npos)) 260 */
220 return -1; 261 long String::toInt (bool* ok, int base) const
221
222 return pos;
223 }
224
225 // -------------------------------------------------------------------------------------------------
226 //
227 int String::find (char ch, int a) const
228 {
229 int pos = m_string.find (ch, a);
230
231 if (pos == int (std::string::npos))
232 return -1;
233
234 return pos;
235 }
236
237 // -------------------------------------------------------------------------------------------------
238 //
239 int String::find_last (const char* c, int a) const
240 {
241 if (a == -1 or a >= length())
242 a = length() - 1;
243
244 for (; a > 0; a--)
245 if (m_string[a] == c[0] and strncmp (chars() + a, c, strlen (c)) == 0)
246 return a;
247
248 return -1;
249 }
250
251 // -------------------------------------------------------------------------------------------------
252 //
253 long String::to_int (bool* ok, int base) const
254 { 262 {
255 errno = 0; 263 errno = 0;
256 char* endptr; 264 char* endPointer;
257 long i = strtol (chars(), &endptr, base); 265 long result = strtol (chars(), &endPointer, base);
258
259 if (ok)
260 *ok = (errno == 0 and *endptr == '\0');
261
262 return i;
263 }
264
265 // -------------------------------------------------------------------------------------------------
266 //
267 float String::to_float (bool* ok) const
268 {
269 errno = 0;
270 char* endptr;
271 float i = (float) strtod (chars(), &endptr);
272 266
273 if (ok != nullptr) 267 if (ok != nullptr)
274 *ok = (errno == 0 and *endptr == '\0'); 268 *ok = (errno == 0 and *endPointer == '\0');
275 269
276 return i; 270 return result;
277 } 271 }
278 272
279 // ------------------------------------------------------------------------------------------------- 273 /*!
280 // 274 * \brief Converts this string to a floating-point number.
281 double String::to_double (bool* ok) const 275 * \param ok An pointer to a boolean to store whether or not the conversion was successful.
276 * If \c ok is \c NULL, the success state is not stored.
277 * \returns the resulting floating-point number.
278 */
279 float String::toFloat (bool* ok) const
280 {
281 return static_cast<float>(toDouble(ok));
282 }
283
284 /*!
285 * \brief Converts this string to a double-precision floating-point number.
286 * \param ok An pointer to a boolean to store whether or not the conversion was successful.
287 * If \c ok is \c NULL, the success state is not stored.
288 * \returns the resulting floating-point number.
289 */
290 double String::toDouble (bool* ok) const
282 { 291 {
283 errno = 0; 292 errno = 0;
284 char* endptr; 293 char* endptr;
285 double i = strtod (chars(), &endptr); 294 double i = strtod (chars(), &endptr);
286 295
288 *ok = (errno == 0 and *endptr == '\0'); 297 *ok = (errno == 0 and *endptr == '\0');
289 298
290 return i; 299 return i;
291 } 300 }
292 301
293 // ------------------------------------------------------------------------------------------------- 302 /*!
294 // 303 * \brief Catenates this string with another string.
295 String String::operator+ (const String& data) const 304 * \param text String to catenate to the end of this string.
305 * \returns the resulting string.
306 */
307 String String::operator+ (const String& text) const
296 { 308 {
297 String newString = *this; 309 String newString = *this;
298 newString.append (data); 310 newString.append (text);
299 return newString; 311 return newString;
300 } 312 }
301 313
302 // ------------------------------------------------------------------------------------------------- 314 /*!
303 // 315 * \brief Catenates this string with another string.
304 String String::operator+ (const char* data) const 316 * \param text String to catenate to the end of this string.
305 { 317 * \returns the resulting string.
306 String newstr = *this; 318 */
307 newstr.append (data); 319 String String::operator+ (const char* text) const
308 return newstr; 320 {
309 } 321 String newString = *this;
310 322 newString.append (text);
311 // ------------------------------------------------------------------------------------------------- 323 return newString;
312 // 324 }
313 bool String::is_numeric() const 325
314 { 326 /*!
315 char* endptr; 327 * \returns whether or not this string represents a number.
316 strtol (chars(), &endptr, 10); 328 */
317 return not (endptr && *endptr); 329 bool String::isNumeric() const
318 } 330 {
319 331 char* endPointer;
320 // ------------------------------------------------------------------------------------------------- 332 strtol (chars(), &endPointer, 10);
321 // 333 return (endPointer != nullptr) and (*endPointer != '\0');
322 bool String::ends_with (const String& other) 334 }
335
336 /*!
337 * \param other Sub-string to find from the end of this string.
338 * \return whether or not this string ends with the provided sub-string.
339 */
340 bool String::endsWith (const String& other) const
341 {
342 if (length() < other.length())
343 {
344 return false;
345 }
346 else
347 {
348 const int offset = length() - other.length();
349 return strncmp (chars() + offset, other.chars(), other.length()) == 0;
350 }
351 }
352
353 /*!
354 * \param other Sub-string to find from the beginning of this string.
355 * \returns whether or not this string begins with the provided sub-string.
356 */
357 bool String::startsWith (const String& other) const
323 { 358 {
324 if (length() < other.length()) 359 if (length() < other.length())
325 return false; 360 return false;
326 361 else
327 const int ofs = length() - other.length(); 362 return strncmp (chars(), other.chars(), other.length()) == 0;
328 return strncmp (chars() + ofs, other.chars(), other.length()) == 0; 363 }
329 } 364
330 365 /*!
331 // ------------------------------------------------------------------------------------------------- 366 * \brief Formats this string using \c printf -like syntax.
332 // 367 * \param formatString Template string to use with formatting.
333 bool String::starts_with (const String& other) const 368 * \param ... Variadic arguments to use with formatting.
334 { 369 */
335 if (length() < other.length()) 370 void __cdecl String::sprintf (const char* formatString, ...)
336 return false;
337
338 return strncmp (chars(), other.chars(), other.length()) == 0;
339 }
340
341 // -------------------------------------------------------------------------------------------------
342 //
343 void __cdecl String::sprintf (const char* fmtstr, ...)
344 { 371 {
345 va_list args; 372 va_list args;
346 va_start (args, fmtstr); 373 va_start (args, formatString);
347 this->vsprintf (fmtstr, args); 374 this->vsprintf (formatString, args);
348 va_end (args); 375 va_end (args);
349 } 376 }
350 377
351 // ------------------------------------------------------------------------------------------------- 378 /*!
352 // 379 * \brief Formats this string using \c vsnprintf, using the provided arguments.
353 void String::vsprintf (const char* fmtstr, va_list args) 380 * \param formatString Template string to use with formatting.
354 { 381 * \param args Variadic arguments to use with formatting.
355 char* buf = nullptr; 382 */
356 int bufsize = 256; 383 void String::vsprintf (const char* formatString, va_list args)
357 384 {
358 do 385 // Copy the argument list so that we have something to provide to vsnprintf in case we have to call it again.
359 { 386 va_list argsCopy;
360 bufsize *= 2; 387 va_copy(argsCopy, args);
361 delete[] buf; 388
362 buf = new char[bufsize]; 389 // First, attempt to format using a fixed-size buffer.
363 } 390 static char buffer[1024];
364 while (vsnprintf (buf, bufsize, fmtstr, args) >= bufsize); 391 size_t length = vsnprintf(buffer, sizeof buffer, formatString, args);
365 392
366 m_string = buf; 393 if (length < sizeof buffer)
367 delete[] buf; 394 {
368 } 395 // vsnprintf succeeded in fitting the formatted string into the buffer, so we're done.
369 396 m_string = buffer;
370 // ------------------------------------------------------------------------------------------------- 397 }
371 // 398 else
372 String StringList::join (const String& delim) 399 {
400 // vsnprintf needs more space, so we have to allocate a new buffer and try again.
401 Vector<char> newBuffer(length + 1);
402 vsnprintf(newBuffer.data(), length + 1, formatString, argsCopy);
403 m_string = newBuffer;
404 }
405 }
406
407 /*!
408 * \brief Joins the elements of this string list into one longer string.
409 * \param delimeter The delimeter to place between the element strings.
410 * \returns the catenated string.
411 */
412 String StringList::join (const String& delimeter)
373 { 413 {
374 String result; 414 String result;
375 415
376 for (const String &item : container()) 416 for (const String &item : container())
377 { 417 {
378 if (result.is_empty() == false) 418 if (result.isEmpty() == false)
379 result += delim; 419 result += delimeter;
380 420
381 result += item; 421 result += item;
382 } 422 }
383 423
384 return result; 424 return result;
385 } 425 }
386 426
387 // ------------------------------------------------------------------------------------------------- 427 /*!
388 // 428 * \brief Tries to match this string against a mask pattern. In the pattern, '?' refers to one character, and '*' to
389 bool String::mask_against (const String& pattern) const 429 * any number of characters.
430 * \param pattern The masking pattern to use for matching.
431 * \returns whether or not this string matches the provided pattern.
432 */
433 bool String::maskAgainst (const String& pattern) const
390 { 434 {
391 // Elevate to uppercase for case-insensitive matching 435 // Elevate to uppercase for case-insensitive matching
392 String pattern_upper = pattern.to_uppercase(); 436 String pattern_upper = pattern.toUpperCase();
393 String this_upper = to_uppercase(); 437 String this_upper = toUpperCase();
394 const char* maskstring = pattern_upper.chars(); 438 const char* maskstring = pattern_upper.chars();
395 const char* mptr = &maskstring[0]; 439 const char* mptr = &maskstring[0];
396 440
397 for (const char* sptr = this_upper.chars(); *sptr != '\0'; sptr++) 441 for (const char* sptr = this_upper.chars(); *sptr != '\0'; sptr++)
398 { 442 {
430 } 474 }
431 475
432 return true; 476 return true;
433 } 477 }
434 478
435 // ------------------------------------------------------------------------------------------------- 479 /*!
436 // 480 * \brief Converts a short integer into a string.
437 String String::from_number (short int a) 481 * \param value The value to convert.
438 { 482 * \returns the resulting string.
439 char buf[32]; 483 */
440 ::sprintf (buf, "%d", a); 484 String String::fromNumber (short int value)
441 return String (buf); 485 {
442 } 486 char buffer[32];
443 487 ::sprintf (buffer, "%d", value);
444 // ------------------------------------------------------------------------------------------------- 488 return String (buffer);
445 // 489 }
446 String String::from_number (int a) 490
447 { 491 /*!
448 char buf[32]; 492 * \brief Converts an integer into a string.
449 ::sprintf (buf, "%d", a); 493 * \param value The value to convert.
450 return String (buf); 494 * \returns the resulting string.
451 } 495 */
452 496 String String::fromNumber (int value)
453 // ------------------------------------------------------------------------------------------------- 497 {
454 // 498 char buffer[32];
455 String String::from_number (long int a) 499 ::sprintf (buffer, "%d", value);
456 { 500 return String (buffer);
457 char buf[32]; 501 }
458 ::sprintf (buf, "%ld", a); 502
459 return String (buf); 503 /*!
460 } 504 * \brief Converts a long integer into a string.
461 505 * \param value The value to convert.
462 // ------------------------------------------------------------------------------------------------- 506 * \returns the resulting string.
463 // 507 */
464 String String::from_number (unsigned short int a) 508 String String::fromNumber (long int value)
465 { 509 {
466 char buf[32]; 510 char buffer[32];
467 ::sprintf (buf, "%u", a); 511 ::sprintf (buffer, "%ld", value);
468 return String (buf); 512 return String (buffer);
469 } 513 }
470 514
471 // ------------------------------------------------------------------------------------------------- 515 /*!
472 // 516 * \brief Converts an unsigned short integer into a string.
473 String String::from_number (unsigned int a) 517 * \param value The value to convert.
474 { 518 * \returns the resulting string.
475 char buf[32]; 519 */
476 ::sprintf (buf, "%u", a); 520 String String::fromNumber (unsigned short int value)
477 return String (buf); 521 {
478 } 522 char buffer[32];
479 523 ::sprintf (buffer, "%u", value);
480 // ------------------------------------------------------------------------------------------------- 524 return String (buffer);
481 // 525 }
482 String String::from_number (unsigned long int a) 526
483 { 527 /*!
484 char buf[32]; 528 * \brief Converts an unsigned integer into a string.
485 ::sprintf (buf, "%lu", a); 529 * \param value The value to convert.
486 return String (buf); 530 * \returns the resulting string.
487 } 531 */
488 532 String String::fromNumber (unsigned int value)
489 // ------------------------------------------------------------------------------------------------- 533 {
490 // 534 char buffer[32];
491 String String::from_number (double a) 535 ::sprintf (buffer, "%u", value);
492 { 536 return String (buffer);
493 char buf[64]; 537 }
494 ::sprintf (buf, "%f", a); 538
495 return String (buf); 539 /*!
496 } 540 * \brief Converts an unsigned long integer into a string.
497 541 * \param value The value to convert.
498 // ------------------------------------------------------------------------------------------------- 542 * \returns the resulting string.
499 // 543 */
544 String String::fromNumber (unsigned long int value)
545 {
546 char buffer[32];
547 ::sprintf (buffer, "%lu", value);
548 return String (buffer);
549 }
550
551 /*!
552 * \brief Converts a double-precision floating point number into a string, using the "%f" format specifier.
553 * \param value The value to convert.
554 * \returns the resulting string.
555 */
556 String String::fromNumber (double value)
557 {
558 char buffer[64];
559 ::sprintf (buffer, "%f", value);
560 return String (buffer);
561 }
562
563 /*!
564 * \returns the MD5-checksum of this string.
565 */
500 String String::md5() const 566 String String::md5() const
501 { 567 {
502 char checksum[33]; 568 char checksum[33];
503 CalculateMD5 (reinterpret_cast<const unsigned char*> (chars()), length(), checksum); 569 CalculateMD5 (reinterpret_cast<const unsigned char*> (chars()), length(), checksum);
504 checksum[sizeof checksum - 1] = '\0'; 570 checksum[sizeof checksum - 1] = '\0';
505 return String (checksum); 571 return String (checksum);
506 } 572 }
507 573
508 // ------------------------------------------------------------------------------------------------- 574 /*!
509 // 575 * \brief Removes leading and trailing whitespace from this string. Alternatively a custom filter can be used to strip
576 * something else than whitespace.
577 * \param filter The filtering function to use.
578 */
510 void String::normalize (int (*filter)(int)) 579 void String::normalize (int (*filter)(int))
511 { 580 {
512 int a = 0; 581 int a = 0;
513 int b = length() - 1; 582 int b = length() - 1;
514 583
522 m_string = ""; 591 m_string = "";
523 else if (a != 0 or b != length() - 1) 592 else if (a != 0 or b != length() - 1)
524 m_string = m_string.substr (a, b - a + 1); 593 m_string = m_string.substr (a, b - a + 1);
525 } 594 }
526 595
527 // ------------------------------------------------------------------------------------------------- 596 /*!
528 // 597 * \returns a version of this string without leading or trailing whitespace. Alternatively a custom filter can be used
598 * to strip something else than whitespace.
599 */
529 String String::normalized (int (*filter)(int)) const 600 String String::normalized (int (*filter)(int)) const
530 { 601 {
531 String result = *this; 602 String result = *this;
532 result.normalize(filter); 603 result.normalize(filter);
533 return result; 604 return result;

mercurial