Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file StringUtils.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Laura Bieker
17 : /// @author Michael Behrisch
18 : /// @author Robert Hilbrich
19 : /// @date unknown
20 : ///
21 : // Some static methods for string processing
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <string>
26 : #include <iostream>
27 : #include <cstdio>
28 : #include <cstring>
29 : #include <regex>
30 : #ifdef WIN32
31 : #define NOMINMAX
32 : #include <windows.h>
33 : #undef NOMINMAX
34 : #else
35 : #include <unistd.h>
36 : #endif
37 : #include <xercesc/util/TransService.hpp>
38 : #include <xercesc/util/TranscodingException.hpp>
39 : #include <utils/common/UtilExceptions.h>
40 : #include <utils/common/ToString.h>
41 : #include <utils/common/StringTokenizer.h>
42 : #include "StringUtils.h"
43 :
44 : #define KM_PER_MILE 1.609344
45 :
46 :
47 : // ===========================================================================
48 : // static member definitions
49 : // ===========================================================================
50 : std::string StringUtils::emptyString;
51 : XERCES_CPP_NAMESPACE::XMLLCPTranscoder* StringUtils::myLCPTranscoder = nullptr;
52 :
53 :
54 : // ===========================================================================
55 : // method definitions
56 : // ===========================================================================
57 : std::string
58 1533029 : StringUtils::prune(const std::string& str) {
59 : const std::string::size_type endpos = str.find_last_not_of(" \t\n\r");
60 1533029 : if (std::string::npos != endpos) {
61 1529892 : const int startpos = (int)str.find_first_not_of(" \t\n\r");
62 1529892 : return str.substr(startpos, endpos - startpos + 1);
63 : }
64 3137 : return "";
65 : }
66 :
67 :
68 : std::string
69 59010 : StringUtils::pruneZeros(const std::string& str, int max) {
70 : const std::string::size_type endpos = str.find_last_not_of("0");
71 59010 : if (endpos != std::string::npos && str.back() == '0') {
72 38645 : std::string res = str.substr(0, MAX2((int)str.size() - max, (int)endpos + 1));
73 38645 : return res;
74 : }
75 : return str;
76 : }
77 :
78 : std::string
79 7742258 : StringUtils::to_lower_case(const std::string& str) {
80 : std::string s = str;
81 : std::transform(s.begin(), s.end(), s.begin(), [](char c) {
82 52829984 : return (char)::tolower(c);
83 : });
84 7742258 : return s;
85 : }
86 :
87 :
88 : std::string
89 1885 : StringUtils::latin1_to_utf8(std::string str) {
90 : // inspired by http://stackoverflow.com/questions/4059775/convert-iso-8859-1-strings-to-utf-8-in-c-c
91 : std::string result;
92 8168 : for (const auto& c : str) {
93 6283 : const unsigned char uc = (unsigned char)c;
94 6283 : if (uc < 128) {
95 : result += uc;
96 : } else {
97 81 : result += (char)(0xc2 + (uc > 0xbf));
98 81 : result += (char)((uc & 0x3f) + 0x80);
99 : }
100 : }
101 1885 : return result;
102 : }
103 :
104 :
105 : std::string
106 1232415 : StringUtils::convertUmlaute(std::string str) {
107 3697245 : str = replace(str, "\xE4", "ae");
108 3697245 : str = replace(str, "\xC4", "Ae");
109 3697245 : str = replace(str, "\xF6", "oe");
110 3697245 : str = replace(str, "\xD6", "Oe");
111 3697245 : str = replace(str, "\xFC", "ue");
112 3697245 : str = replace(str, "\xDC", "Ue");
113 3697245 : str = replace(str, "\xDF", "ss");
114 3697245 : str = replace(str, "\xC9", "E");
115 3697245 : str = replace(str, "\xE9", "e");
116 3697245 : str = replace(str, "\xC8", "E");
117 3697245 : str = replace(str, "\xE8", "e");
118 1232415 : return str;
119 : }
120 :
121 :
122 : std::string
123 66249210 : StringUtils::replace(std::string str, const std::string& what, const std::string& by) {
124 : std::string::size_type idx = str.find(what);
125 66249210 : const int what_len = (int)what.length();
126 66249210 : if (what_len > 0) {
127 66249209 : const int by_len = (int)by.length();
128 66252945 : while (idx != std::string::npos) {
129 3736 : str = str.replace(idx, what_len, by);
130 3736 : idx = str.find(what, idx + by_len);
131 : }
132 : }
133 66249210 : return str;
134 : }
135 :
136 :
137 : std::string
138 1114733 : StringUtils::substituteEnvironment(const std::string& str, const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
139 : std::string s = str;
140 1114733 : if (timeRef != nullptr) {
141 : const std::string::size_type localTimeIndex = str.find("${LOCALTIME}");
142 : const std::string::size_type utcIndex = str.find("${UTC}");
143 : const bool isUTC = utcIndex != std::string::npos;
144 1114727 : if (localTimeIndex != std::string::npos || isUTC) {
145 6 : const time_t rawtime = std::chrono::system_clock::to_time_t(*timeRef);
146 : char buffer [80];
147 6 : struct tm* timeinfo = isUTC ? gmtime(&rawtime) : localtime(&rawtime);
148 6 : strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S.", timeinfo);
149 : auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(*timeRef);
150 : auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(*timeRef - seconds);
151 6 : const std::string micro = buffer + toString(microseconds.count());
152 6 : if (isUTC) {
153 : s.replace(utcIndex, 6, micro);
154 : } else {
155 : s.replace(localTimeIndex, 12, micro);
156 : }
157 : }
158 : }
159 : const std::string::size_type pidIndex = str.find("${PID}");
160 1114733 : if (pidIndex != std::string::npos) {
161 : #ifdef WIN32
162 : s.replace(pidIndex, 6, toString(::GetCurrentProcessId()));
163 : #else
164 6 : s.replace(pidIndex, 6, toString(::getpid()));
165 : #endif
166 : }
167 1114733 : if (std::getenv("SUMO_LOGO") == nullptr) {
168 3344199 : s = replace(s, "${SUMO_LOGO}", "${SUMO_HOME}/data/logo/sumo-128x138.png");
169 : }
170 : const std::string::size_type tildeIndex = str.find("~");
171 1114733 : if (tildeIndex == 0) {
172 3 : s.replace(0, 1, "${HOME}");
173 : }
174 3344199 : s = replace(s, ",~", ",${HOME}");
175 : #ifdef WIN32
176 : if (std::getenv("HOME") == nullptr) {
177 : s = replace(s, "${HOME}", "${USERPROFILE}");
178 : }
179 : #endif
180 :
181 : // Expression for an environment variables, e.g. ${NAME}
182 : // Note: - R"(...)" is a raw string literal syntax to simplify a regex declaration
183 : // - .+? looks for the shortest match (non-greedy)
184 : // - (.+?) defines a "subgroup" which is already stripped of the $ and {, }
185 1114733 : std::regex envVarExpr(R"(\$\{(.+?)\})");
186 :
187 : // Are there any variables in this string?
188 : std::smatch match;
189 : std::string strIter = s;
190 :
191 : // Loop over the entire value string and look for variable names
192 1114799 : while (std::regex_search(strIter, match, envVarExpr)) {
193 : std::string varName = match[1];
194 :
195 : // Find the variable in the environment and its value
196 : std::string varValue;
197 66 : if (std::getenv(varName.c_str()) != nullptr) {
198 65 : varValue = std::getenv(varName.c_str());
199 : }
200 :
201 : // Replace the variable placeholder with its value in the original string
202 264 : s = std::regex_replace(s, std::regex("\\$\\{" + varName + "\\}"), varValue);
203 :
204 : // Continue the loop with the remainder of the string
205 132 : strIter = match.suffix();
206 : }
207 1114733 : return s;
208 1114733 : }
209 :
210 :
211 : std::string
212 79701 : StringUtils::isoTimeString(const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
213 79701 : const std::chrono::system_clock::time_point now = timeRef == nullptr ? std::chrono::system_clock::now() : *timeRef;
214 : const auto now_seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
215 79701 : const std::time_t now_c = std::chrono::system_clock::to_time_t(now);
216 : const auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - now_seconds).count();
217 79701 : std::tm local_tm = *std::localtime(&now_c);
218 :
219 : // Get the time zone offset
220 79701 : std::time_t utc_time = std::time(nullptr);
221 79701 : std::tm utc_tm = *std::gmtime(&utc_time);
222 79701 : const double offset = std::difftime(std::mktime(&local_tm), std::mktime(&utc_tm)) / 3600.0;
223 79701 : const int hours_offset = static_cast<int>(offset);
224 79701 : const int minutes_offset = static_cast<int>((offset - hours_offset) * 60);
225 :
226 : // Format the time
227 79701 : std::ostringstream oss;
228 : char buf[32];
229 79701 : std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &local_tm);
230 : oss << buf << "."
231 : << std::setw(6) << std::setfill('0') << std::abs(microseconds)
232 : << (hours_offset >= 0 ? "+" : "-")
233 79701 : << std::setw(2) << std::setfill('0') << std::abs(hours_offset) << ":"
234 159402 : << std::setw(2) << std::setfill('0') << std::abs(minutes_offset);
235 79701 : return oss.str();
236 79701 : }
237 :
238 :
239 : bool
240 6318283 : StringUtils::startsWith(const std::string& str, const std::string prefix) {
241 6318283 : return str.compare(0, prefix.length(), prefix) == 0;
242 : }
243 :
244 :
245 : bool
246 220288 : StringUtils::endsWith(const std::string& str, const std::string suffix) {
247 220288 : if (str.length() >= suffix.length()) {
248 220266 : return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
249 : } else {
250 : return false;
251 : }
252 : }
253 :
254 :
255 : std::string
256 0 : StringUtils::padFront(const std::string& str, int length, char padding) {
257 0 : return std::string(MAX2(0, length - (int)str.size()), padding) + str;
258 : }
259 :
260 :
261 : std::string
262 1379785 : StringUtils::escapeXML(const std::string& orig, const bool maskDoubleHyphen) {
263 4139355 : std::string result = replace(orig, "&", "&");
264 4139355 : result = replace(result, ">", ">");
265 4139355 : result = replace(result, "<", "<");
266 4139355 : result = replace(result, "\"", """);
267 1379785 : if (maskDoubleHyphen) {
268 2368242 : result = replace(result, "--", "--");
269 : }
270 44153120 : for (char invalid = '\1'; invalid < ' '; invalid++) {
271 213866675 : result = replace(result, std::string(1, invalid).c_str(), "");
272 : }
273 4139355 : return replace(result, "'", "'");
274 : }
275 :
276 :
277 : std::string
278 0 : StringUtils::escapeShell(const std::string& orig) {
279 0 : std::string result = replace(orig, "\"", "\\\"");
280 0 : return result;
281 : }
282 :
283 :
284 : std::string
285 5121 : StringUtils::urlEncode(const std::string& toEncode, const std::string encodeWhich) {
286 5121 : std::ostringstream out;
287 :
288 131262 : for (int i = 0; i < (int)toEncode.length(); ++i) {
289 126141 : const char t = toEncode.at(i);
290 :
291 126141 : if ((encodeWhich != "" && encodeWhich.find(t) == std::string::npos) ||
292 1 : (encodeWhich == "" &&
293 0 : ((t >= 45 && t <= 57) || // hyphen, period, slash, 0-9
294 0 : (t >= 65 && t <= 90) || // A-Z
295 : t == 95 || // underscore
296 : (t >= 97 && t <= 122) || // a-z
297 : t == 126)) // tilde
298 : ) {
299 126140 : out << toEncode.at(i);
300 : } else {
301 2 : out << charToHex(toEncode.at(i));
302 : }
303 : }
304 :
305 5121 : return out.str();
306 5121 : }
307 :
308 :
309 : std::string
310 78499 : StringUtils::urlDecode(const std::string& toDecode) {
311 78499 : std::ostringstream out;
312 :
313 1177327 : for (int i = 0; i < (int)toDecode.length(); ++i) {
314 1098830 : if (toDecode.at(i) == '%') {
315 2 : std::string str(toDecode.substr(i + 1, 2));
316 2 : out << hexToChar(str);
317 0 : i += 2;
318 : } else {
319 1098828 : out << toDecode.at(i);
320 : }
321 : }
322 :
323 78497 : return out.str();
324 78499 : }
325 :
326 : std::string
327 1 : StringUtils::charToHex(unsigned char c) {
328 : short i = c;
329 :
330 1 : std::stringstream s;
331 :
332 1 : s << "%" << std::setw(2) << std::setfill('0') << std::hex << i;
333 :
334 1 : return s.str();
335 1 : }
336 :
337 :
338 : unsigned char
339 2 : StringUtils::hexToChar(const std::string& str) {
340 2 : short c = 0;
341 2 : if (!str.empty()) {
342 2 : std::istringstream in(str);
343 2 : in >> std::hex >> c;
344 2 : if (in.fail()) {
345 4 : throw NumberFormatException(str + " could not be interpreted as hex");
346 : }
347 2 : }
348 0 : return static_cast<unsigned char>(c);
349 : }
350 :
351 :
352 : int
353 17580381 : StringUtils::toInt(const std::string& sData) {
354 17580381 : long long int result = toLong(sData);
355 17566075 : if (result > std::numeric_limits<int>::max() || result < std::numeric_limits<int>::min()) {
356 3 : throw NumberFormatException(toString(result) + " int overflow");
357 : }
358 17566074 : return (int)result;
359 : }
360 :
361 :
362 : bool
363 8 : StringUtils::isInt(const std::string& sData) {
364 : // first check if can be converted to long int
365 8 : if (isLong(sData)) {
366 0 : const long long int result = toLong(sData);
367 : // now check if the result is in the range of an int
368 0 : return ((result <= std::numeric_limits<int>::max()) && (result >= std::numeric_limits<int>::min()));
369 : }
370 : return false;
371 : }
372 :
373 :
374 : int
375 0 : StringUtils::toIntSecure(const std::string& sData, int def) {
376 0 : if (sData.length() == 0) {
377 : return def;
378 : }
379 0 : return toInt(sData);
380 : }
381 :
382 :
383 : long long int
384 19001501 : StringUtils::toLong(const std::string& sData) {
385 : const char* const data = sData.c_str();
386 19001501 : if (data == 0 || data[0] == 0) {
387 64 : throw EmptyData();
388 : }
389 : char* end;
390 19001437 : errno = 0;
391 : #ifdef _MSC_VER
392 : long long int ret = _strtoi64(data, &end, 10);
393 : #else
394 19001437 : long long int ret = strtoll(data, &end, 10);
395 : #endif
396 19001437 : if (errno == ERANGE) {
397 0 : errno = 0;
398 0 : throw NumberFormatException("(long long integer range) " + sData);
399 : }
400 19001437 : if ((int)(end - data) != (int)strlen(data)) {
401 48552 : throw NumberFormatException("(long long integer format) " + sData);
402 : }
403 18977161 : return ret;
404 : }
405 :
406 :
407 : bool
408 8 : StringUtils::isLong(const std::string& sData) {
409 : const char* const data = sData.c_str();
410 8 : if (data == 0 || data[0] == 0) {
411 : return false;
412 : }
413 : char* end;
414 : // reset errno before parsing, to keep errors
415 8 : errno = 0;
416 : // continue depending of current plattform
417 : #ifdef _MSC_VER
418 : _strtoi64(data, &end, 10);
419 : #else
420 8 : strtoll(data, &end, 10);
421 : #endif
422 : // check out of range
423 8 : if (errno == ERANGE) {
424 : return false;
425 : }
426 : // check length of converted data
427 8 : if ((int)(end - data) != (int)strlen(data)) {
428 : return false;
429 : }
430 : return true;
431 : }
432 :
433 :
434 : int
435 937 : StringUtils::hexToInt(const std::string& sData) {
436 937 : if (sData.length() == 0) {
437 0 : throw EmptyData();
438 : }
439 937 : size_t idx = 0;
440 : int result;
441 : try {
442 937 : if (sData[0] == '#') { // for html color codes
443 853 : result = std::stoi(sData.substr(1), &idx, 16);
444 853 : idx++;
445 : } else {
446 : result = std::stoi(sData, &idx, 16);
447 : }
448 0 : } catch (...) {
449 0 : throw NumberFormatException("(hex integer format) " + sData);
450 0 : }
451 937 : if (idx != sData.length()) {
452 0 : throw NumberFormatException("(hex integer format) " + sData);
453 : }
454 937 : return result;
455 : }
456 :
457 :
458 : bool
459 0 : StringUtils::isHex(std::string sData) {
460 0 : if (sData.length() == 0) {
461 : return false;
462 : }
463 : // remove the first character (for HTML color codes)
464 0 : if (sData[0] == '#') {
465 0 : sData = sData.substr(1);
466 : }
467 : const char* sDataPtr = sData.c_str();
468 : char* returnPtr;
469 : // reset errno
470 0 : errno = 0;
471 : // call string to long (size 16) from standard library
472 0 : strtol(sDataPtr, &returnPtr, 16);
473 : // check out of range
474 0 : if (errno == ERANGE) {
475 : return false;
476 : }
477 : // check if there was an error converting sDataPtr to double,
478 0 : if (sDataPtr == returnPtr) {
479 : return false;
480 : }
481 : // compare size of start and end points
482 0 : if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
483 : return false;
484 : }
485 : return true;
486 : }
487 :
488 :
489 : double
490 72536743 : StringUtils::toDouble(const std::string& sData) {
491 72536743 : if (sData.size() == 0) {
492 191 : throw EmptyData();
493 : }
494 : try {
495 72536552 : size_t idx = 0;
496 : const double result = std::stod(sData, &idx);
497 72534976 : if (idx != sData.size()) {
498 34 : throw NumberFormatException("(double format) " + sData);
499 : } else {
500 72534959 : return result;
501 : }
502 1593 : } catch (...) {
503 : // invalid_argument or out_of_range
504 3186 : throw NumberFormatException("(double) " + sData);
505 1593 : }
506 : }
507 :
508 :
509 : bool
510 0 : StringUtils::isDouble(const std::string& sData) {
511 0 : if (sData.size() == 0) {
512 : return false;
513 : }
514 : const char* sDataPtr = sData.c_str();
515 : char* returnPtr;
516 : // reset errno
517 0 : errno = 0;
518 : // call string to double from standard library
519 0 : strtod(sDataPtr, &returnPtr);
520 : // check out of range
521 0 : if (errno == ERANGE) {
522 : return false;
523 : }
524 : // check if there was an error converting sDataPtr to double,
525 0 : if (sDataPtr == returnPtr) {
526 : return false;
527 : }
528 : // compare size of start and end points
529 0 : if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
530 : return false;
531 : }
532 : return true;
533 : }
534 :
535 :
536 : double
537 344 : StringUtils::toDoubleSecure(const std::string& sData, const double def) {
538 344 : if (sData.length() == 0) {
539 : return def;
540 : }
541 344 : return toDouble(sData);
542 : }
543 :
544 :
545 : bool
546 3321899 : StringUtils::toBool(const std::string& sData) {
547 3321899 : if (sData.length() == 0) {
548 1 : throw EmptyData();
549 : }
550 3321898 : const std::string s = to_lower_case(sData);
551 3321898 : if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
552 : return true;
553 : }
554 2761913 : if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
555 : return false;
556 : }
557 63 : throw BoolFormatException(s);
558 : }
559 :
560 :
561 : bool
562 0 : StringUtils::isBool(const std::string& sData) {
563 0 : if (sData.length() == 0) {
564 : return false;
565 : }
566 0 : const std::string s = to_lower_case(sData);
567 : // check true values
568 0 : if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
569 : return true;
570 : }
571 : // check false values
572 0 : if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
573 : return true;
574 : }
575 : // no valid true or false values
576 : return false;
577 : }
578 :
579 :
580 : MMVersion
581 43962 : StringUtils::toVersion(const std::string& sData) {
582 131886 : std::vector<std::string> parts = StringTokenizer(sData, ".").getVector();
583 87924 : return MMVersion(toInt(parts.front()), toDouble(parts.back()));
584 43962 : }
585 :
586 :
587 : double
588 2197 : StringUtils::parseDist(const std::string& sData) {
589 2197 : if (sData.size() == 0) {
590 1 : throw EmptyData();
591 : }
592 : try {
593 2196 : size_t idx = 0;
594 : const double result = std::stod(sData, &idx);
595 2195 : if (idx != sData.size()) {
596 4 : const std::string unit = prune(sData.substr(idx));
597 4 : if (unit == "m" || unit == "metre" || unit == "meter" || unit == "metres" || unit == "meters") {
598 : return result;
599 : }
600 2 : if (unit == "km" || unit == "kilometre" || unit == "kilometer" || unit == "kilometres" || unit == "kilometers") {
601 1 : return result * 1000.;
602 : }
603 1 : if (unit == "mi" || unit == "mile" || unit == "miles") {
604 0 : return result * 1000. * KM_PER_MILE;
605 : }
606 1 : if (unit == "nmi") {
607 0 : return result * 1852.;
608 : }
609 1 : if (unit == "ft" || unit == "foot" || unit == "feet") {
610 0 : return result * 12. * 0.0254;
611 : }
612 1 : if (unit == "\"" || unit == "in" || unit == "inch" || unit == "inches") {
613 0 : return result * 0.0254;
614 : }
615 1 : if (unit[0] == '\'') {
616 0 : double inches = 12 * result;
617 0 : if (unit.length() > 1) {
618 0 : inches += std::stod(unit.substr(1), &idx);
619 1 : if (unit.substr(idx) == "\"") {
620 0 : return inches * 0.0254;
621 : }
622 : }
623 : }
624 2 : throw NumberFormatException("(distance format) " + sData);
625 : } else {
626 : return result;
627 : }
628 2 : } catch (...) {
629 : // invalid_argument or out_of_range
630 4 : throw NumberFormatException("(double) " + sData);
631 2 : }
632 : }
633 :
634 :
635 : double
636 7117 : StringUtils::parseSpeed(const std::string& sData, const bool defaultKmph) {
637 7117 : if (sData.size() == 0) {
638 1 : throw EmptyData();
639 : }
640 : try {
641 7116 : size_t idx = 0;
642 : const double result = std::stod(sData, &idx);
643 7112 : if (idx != sData.size()) {
644 583 : const std::string unit = prune(sData.substr(idx));
645 583 : if (unit == "km/h" || unit == "kph" || unit == "kmh" || unit == "kmph") {
646 3 : return result / 3.6;
647 : }
648 580 : if (unit == "m/s") {
649 : return result;
650 : }
651 578 : if (unit == "mph") {
652 577 : return result * KM_PER_MILE / 3.6;
653 : }
654 1 : if (unit == "knots") {
655 0 : return result * 1.852 / 3.6;
656 : }
657 2 : throw NumberFormatException("(speed format) " + sData);
658 : } else {
659 6529 : return defaultKmph ? result / 3.6 : result;
660 : }
661 5 : } catch (...) {
662 : // invalid_argument or out_of_range
663 10 : throw NumberFormatException("(double) " + sData);
664 5 : }
665 : }
666 :
667 :
668 : std::string
669 94843465 : StringUtils::transcode(const XMLCh* const data, int length) {
670 94843465 : if (data == 0) {
671 0 : throw EmptyData();
672 : }
673 94843465 : if (length == 0) {
674 342651 : return "";
675 : }
676 : #if _XERCES_VERSION < 30100
677 : char* t = XERCES_CPP_NAMESPACE::XMLString::transcode(data);
678 : std::string result(t);
679 : XERCES_CPP_NAMESPACE::XMLString::release(&t);
680 : return result;
681 : #else
682 : try {
683 94500814 : XERCES_CPP_NAMESPACE::TranscodeToStr utf8(data, "UTF-8");
684 94500814 : return reinterpret_cast<const char*>(utf8.str());
685 94500814 : } catch (XERCES_CPP_NAMESPACE::TranscodingException&) {
686 0 : return "?";
687 0 : }
688 : #endif
689 : }
690 :
691 :
692 : std::string
693 659685 : StringUtils::transcodeFromLocal(const std::string& localString) {
694 : #if _XERCES_VERSION > 30100
695 : try {
696 659685 : if (myLCPTranscoder == nullptr) {
697 47681 : myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
698 : }
699 659685 : if (myLCPTranscoder != nullptr) {
700 659685 : return transcode(myLCPTranscoder->transcode(localString.c_str()));
701 : }
702 0 : } catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
703 : #endif
704 : return localString;
705 : }
706 :
707 :
708 : std::string
709 813183 : StringUtils::transcodeToLocal(const std::string& utf8String) {
710 : #if _XERCES_VERSION > 30100
711 : try {
712 813183 : if (myLCPTranscoder == nullptr) {
713 1115 : myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
714 : }
715 813183 : if (myLCPTranscoder != nullptr) {
716 813183 : XERCES_CPP_NAMESPACE::TranscodeFromStr utf8(reinterpret_cast<const XMLByte*>(utf8String.c_str()), utf8String.size(), "UTF-8");
717 813183 : return myLCPTranscoder->transcode(utf8.str());
718 813183 : }
719 0 : } catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
720 : #endif
721 : return utf8String;
722 : }
723 :
724 :
725 : std::string
726 0 : StringUtils::trim_left(const std::string s, const std::string& t) {
727 : std::string result = s;
728 0 : result.erase(0, s.find_first_not_of(t));
729 0 : return result;
730 : }
731 :
732 : std::string
733 0 : StringUtils::trim_right(const std::string s, const std::string& t) {
734 : std::string result = s;
735 0 : result.erase(s.find_last_not_of(t) + 1);
736 0 : return result;
737 : }
738 :
739 : std::string
740 0 : StringUtils::trim(const std::string s, const std::string& t) {
741 0 : return trim_right(trim_left(s, t), t);
742 : }
743 :
744 :
745 : std::string
746 0 : StringUtils::wrapText(const std::string s, int width) {
747 0 : std::vector<std::string> parts = StringTokenizer(s).getVector();
748 : std::string result;
749 : std::string line;
750 : bool firstLine = true;
751 : bool firstWord = true;
752 0 : for (std::string p : parts) {
753 0 : if ((int)(line.size() + p.size()) < width || firstWord) {
754 0 : if (firstWord) {
755 : firstWord = false;
756 : } else {
757 : line += " ";
758 : }
759 0 : line = line + p;
760 : } else {
761 0 : if (firstLine) {
762 : firstLine = false;
763 : } else {
764 : result += "\n";
765 : }
766 0 : result = result + line;
767 : line.clear();
768 : firstWord = true;
769 : }
770 : }
771 0 : if (line.size() > 0) {
772 0 : if (firstLine) {
773 : firstLine = false;
774 : } else {
775 : result += "\n";
776 : }
777 0 : result = result + line;
778 : }
779 0 : return result;
780 0 : }
781 :
782 :
783 : void
784 49156 : StringUtils::resetTranscoder() {
785 49156 : myLCPTranscoder = nullptr;
786 49156 : }
787 :
788 :
789 : std::string
790 955 : StringUtils::adjustDecimalValue(double value, int precision) {
791 : // obtain value in string format with 20 decimals precision
792 955 : auto valueStr = toString(value, precision);
793 : // now clear all zeros
794 16759 : while (valueStr.size() > 1) {
795 16759 : if (valueStr.back() == '0') {
796 : valueStr.pop_back();
797 955 : } else if (valueStr.back() == '.') {
798 : valueStr.pop_back();
799 : return valueStr;
800 : } else {
801 : return valueStr;
802 : }
803 : }
804 : return valueStr;
805 : }
806 :
807 : /****************************************************************************/
|