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