Showing the changes to Source/Libraries/TimeSeriesPlatformLibrary/Common/Convert.cpp (1 of 17 files). Show all
Unified · Split
| @@ -25,11 +25,12 @@ · 24 unmodified lines | |||
| 25 | #include <sstream> | 25 | #include <sstream> |
| 26 | #include <locale> | 26 | #include <locale> |
| 27 | #include <codecvt> | 27 | #include <codecvt> |
| 28 | #include <boost/uuid/string_generator.hpp> | ||
| 29 | #include <boost/uuid/uuid_io.hpp> | 28 | #include <boost/uuid/uuid_io.hpp> |
| 29 | #include <boost/uuid/string_generator.hpp> | ||
| 30 | #include <boost/date_time/posix_time/posix_time.hpp> | 30 | #include <boost/date_time/posix_time/posix_time.hpp> |
| 31 | #include <boost/date_time/gregorian/gregorian.hpp> | 31 | #include <boost/date_time/gregorian/gregorian.hpp> |
| 32 | #include <boost/date_time/c_local_time_adjustor.hpp> | 32 | #include <boost/date_time/c_local_time_adjustor.hpp> |
| 33 | #include <boost/algorithm/string.hpp> | ||
| 33 | #include "Convert.h" | 34 | #include "Convert.h" |
| 34 | 35 | ||
| 35 | using namespace std; | 36 | using namespace std; |
| @@ -232,7 +233,7 @@ uint32_t GSF::TicksToString(char* ptr, uint32_t maxsize, string format, int64_t · 196 unmodified lines | |||
| 232 | 233 | ||
| 233 | DateTime GSF::LocalFromUtc(const DateTime& timestamp) | 234 | DateTime GSF::LocalFromUtc(const DateTime& timestamp) |
| 234 | { | 235 | { |
| 235 | return DateTime(boost::date_time::c_local_adjustor<DateTime>::utc_to_local(timestamp)); | 236 | return boost::date_time::c_local_adjustor<DateTime>::utc_to_local(timestamp); |
| 236 | } | 237 | } |
| 237 | 238 | ||
| 238 | std::string GSF::ToString(const Guid& value) | 239 | std::string GSF::ToString(const Guid& value) |
| @@ -273,6 +274,48 @@ string GSF::ToUTF8(const wstring& value) · 34 unmodified lines | |||
| 273 | return converter.to_bytes(value); | 274 | return converter.to_bytes(value); |
| 274 | } | 275 | } |
| 275 | 276 | ||
| 277 | bool GSF::ParseBoolean(const string& value) | ||
| 278 | { | ||
| 279 | if (value.empty()) | ||
| 280 | return false; | ||
| 281 | |||
| 282 | const string result = Trim(value); | ||
| 283 | |||
| 284 | if (result.empty()) | ||
| 285 | { | ||
| 286 | if (IsEqual(result, "true")) | ||
| 287 | return true; | ||
| 288 | |||
| 289 | if (IsEqual(result, "false")) | ||
| 290 | return false; | ||
| 291 | |||
| 292 | try | ||
| 293 | { | ||
| 294 | return stoi(result) != 0; | ||
| 295 | } | ||
| 296 | catch (...) | ||
| 297 | { | ||
| 298 | const char first = toupper(result[0]); | ||
| 299 | return first == 'T' || first == 'Y'; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | return false; | ||
| 304 | } | ||
| 305 | |||
| 306 | bool GSF::TryParseDouble(const string& value, float64_t& result) | ||
| 307 | { | ||
| 308 | try | ||
| 309 | { | ||
| 310 | result = stod(value); | ||
| 311 | return true; | ||
| 312 | } | ||
| 313 | catch (...) | ||
| 314 | { | ||
| 315 | return false; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 276 | std::string GSF::RegExEncode(const char value) | 319 | std::string GSF::RegExEncode(const char value) |
| 277 | { | 320 | { |
| 278 | std::stringstream stream; | 321 | std::stringstream stream; |
| @@ -380,3 +423,139 @@ DateTime GSF::ParseTimestamp(const char* time, bool parseAsUTC) · 101 unmodified lines | |||
| 380 | 423 | ||
| 381 | throw runtime_error("Failed to parse timestamp \"" + string(time) + "\""); | 424 | throw runtime_error("Failed to parse timestamp \"" + string(time) + "\""); |
| 382 | } | 425 | } |
| 426 | |||
| 427 | StringMap<string> GSF::ParseKeyValuePairs(const string& value, const char parameterDelimiter, const char keyValueDelimiter, const char startValueDelimiter, const char endValueDelimiter) | ||
| 428 | { | ||
| 429 | if (parameterDelimiter == keyValueDelimiter || | ||
| 430 | parameterDelimiter == startValueDelimiter || | ||
| 431 | parameterDelimiter == endValueDelimiter || | ||
| 432 | keyValueDelimiter == startValueDelimiter || | ||
| 433 | keyValueDelimiter == endValueDelimiter || | ||
| 434 | startValueDelimiter == endValueDelimiter) | ||
| 435 | throw invalid_argument("All delimiters must be unique"); | ||
| 436 | |||
| 437 | const string& escapedParameterDelimiter = RegExEncode(parameterDelimiter); | ||
| 438 | const string& escapedKeyValueDelimiter = RegExEncode(keyValueDelimiter); | ||
| 439 | const string& escapedStartValueDelimiter = RegExEncode(startValueDelimiter); | ||
| 440 | const string& escapedEndValueDelimiter = RegExEncode(endValueDelimiter); | ||
| 441 | const string& escapedBackslashDelimiter = RegExEncode('\\'); | ||
| 442 | const string& parameterDelimiterStr = string(1, parameterDelimiter); | ||
| 443 | const string& keyValueDelimiterStr = string(1, keyValueDelimiter); | ||
| 444 | const string& startValueDelimiterStr = string(1, startValueDelimiter); | ||
| 445 | const string& endValueDelimiterStr = string(1, endValueDelimiter); | ||
| 446 | const string& backslashDelimiterStr = "\\"; | ||
| 447 | |||
| 448 | StringMap<string> keyValuePairs; | ||
| 449 | vector<string> escapedValue; | ||
| 450 | bool valueEscaped = false; | ||
| 451 | uint32_t delimiterDepth = 0; | ||
| 452 | |||
| 453 | // Escape any parameter or key/value delimiters within tagged value sequences | ||
| 454 | // For example, the following string: | ||
| 455 | // "normalKVP=-1; nestedKVP={p1=true; p2=false}") | ||
| 456 | // would be encoded as: | ||
| 457 | // "normalKVP=-1; nestedKVP=p1\\u003dtrue\\u003b p2\\u003dfalse") | ||
| 458 | for (uint32_t i = 0; i < value.size(); i++) | ||
| 459 | { | ||
| 460 | const char character = value[i]; | ||
| 461 | |||
| 462 | if (character == startValueDelimiter) | ||
| 463 | { | ||
| 464 | if (!valueEscaped) | ||
| 465 | { | ||
| 466 | valueEscaped = true; | ||
| 467 | continue; // Don't add tag start delimiter to final value | ||
| 468 | } | ||
| 469 | |||
| 470 | // Handle nested delimiters | ||
| 471 | delimiterDepth++; | ||
| 472 | } | ||
| 473 | |||
| 474 | if (character == endValueDelimiter) | ||
| 475 | { | ||
| 476 | if (valueEscaped) | ||
| 477 | { | ||
| 478 | if (delimiterDepth > 0) | ||
| 479 | { | ||
| 480 | // Handle nested delimiters | ||
| 481 | delimiterDepth--; | ||
| 482 | } | ||
| 483 | else | ||
| 484 | { | ||
| 485 | valueEscaped = false; | ||
| 486 | continue; // Don't add tag stop delimiter to final value | ||
| 487 | } | ||
| 488 | } | ||
| 489 | else | ||
| 490 | { | ||
| 491 | throw runtime_error("Failed to parse key/value pairs: invalid delimiter mismatch. Encountered end value delimiter '" + endValueDelimiterStr + "' before start value delimiter '" + startValueDelimiterStr + "'."); // NOLINT | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | if (valueEscaped) | ||
| 496 | { | ||
| 497 | // Escape any delimiter characters inside nested key/value pair | ||
| 498 | if (character == parameterDelimiter) | ||
| 499 | escapedValue.push_back(escapedParameterDelimiter); | ||
| 500 | else if (character == keyValueDelimiter) | ||
| 501 | escapedValue.push_back(escapedKeyValueDelimiter); | ||
| 502 | else if (character == startValueDelimiter) | ||
| 503 | escapedValue.push_back(escapedStartValueDelimiter); | ||
| 504 | else if (character == endValueDelimiter) | ||
| 505 | escapedValue.push_back(escapedEndValueDelimiter); | ||
| 506 | else if (character == '\\') | ||
| 507 | escapedValue.push_back(escapedBackslashDelimiter); | ||
| 508 | else | ||
| 509 | escapedValue.emplace_back(1, character); | ||
| 510 | } | ||
| 511 | else | ||
| 512 | { | ||
| 513 | if (character == '\\') | ||
| 514 | escapedValue.push_back(escapedBackslashDelimiter); | ||
| 515 | else | ||
| 516 | escapedValue.emplace_back(1, character); | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | if (delimiterDepth != 0 || valueEscaped) | ||
| 521 | { | ||
| 522 | // If value is still escaped, tagged expression was not terminated | ||
| 523 | if (valueEscaped) | ||
| 524 | delimiterDepth = 1; | ||
| 525 | |||
| 526 | const bool moreStartDelimiters = delimiterDepth > 0; | ||
| 527 | |||
| 528 | throw runtime_error( | ||
| 529 | "Failed to parse key/value pairs: invalid delimiter mismatch. Encountered more " + | ||
| 530 | (moreStartDelimiters ? "start value delimiters '" + startValueDelimiterStr + "'" : "end value delimiters '" + endValueDelimiterStr + "'") + " than " + | ||
| 531 | (moreStartDelimiters ? "end value delimiters '" + endValueDelimiterStr + "'" : "start value delimiters '" + startValueDelimiterStr + "'") + "."); | ||
| 532 | } | ||
| 533 | |||
| 534 | // Parse key/value pairs from escaped value | ||
| 535 | vector<string> pairs = Split(boost::algorithm::join(escapedValue, ""), parameterDelimiterStr, false); | ||
| 536 | |||
| 537 | for (uint32_t i = 0; i < pairs.size(); i++) | ||
| 538 | { | ||
| 539 | // Separate key from value | ||
| 540 | vector<string> elements = Split(pairs[i], keyValueDelimiterStr, false); | ||
| 541 | |||
| 542 | if (elements.size() == 2) | ||
| 543 | { | ||
| 544 | // Get key | ||
| 545 | const string key = Trim(elements[0]); | ||
| 546 | |||
| 547 | // Get unescaped value | ||
| 548 | const string unescapedValue = Replace(Replace(Replace(Replace(Replace(Trim(elements[1]), | ||
| 549 | escapedParameterDelimiter, parameterDelimiterStr, false), | ||
| 550 | escapedKeyValueDelimiter, keyValueDelimiterStr, false), | ||
| 551 | escapedStartValueDelimiter, startValueDelimiterStr, false), | ||
| 552 | escapedEndValueDelimiter, endValueDelimiterStr, false), | ||
| 553 | escapedBackslashDelimiter, backslashDelimiterStr, false); | ||
| 554 | |||
| 555 | // Add or replace key elements with unescaped value | ||
| 556 | keyValuePairs[key] = unescapedValue; | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | return keyValuePairs; | ||
| 561 | } | ||