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 | 25 | #include <sstream> |
| 26 | 26 | #include <locale> |
| 27 | 27 | #include <codecvt> |
| 28 | −#include <boost/uuid/string_generator.hpp> | |
| 29 | 28 | #include <boost/uuid/uuid_io.hpp> |
| 29 | +#include <boost/uuid/string_generator.hpp> | |
| 30 | 30 | #include <boost/date_time/posix_time/posix_time.hpp> |
| 31 | 31 | #include <boost/date_time/gregorian/gregorian.hpp> |
| 32 | 32 | #include <boost/date_time/c_local_time_adjustor.hpp> |
| 33 | +#include <boost/algorithm/string.hpp> | |
| 33 | 34 | #include "Convert.h" |
| 34 | 35 | |
| 35 | 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 | 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 | 239 | std::string GSF::ToString(const Guid& value) |
| @@ -273,6 +274,48 @@ string GSF::ToUTF8(const wstring& value) · 34 unmodified lines | ||
| 273 | 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 | 319 | std::string GSF::RegExEncode(const char value) |
| 277 | 320 | { |
| 278 | 321 | std::stringstream stream; |
| @@ -380,3 +423,139 @@ DateTime GSF::ParseTimestamp(const char* time, bool parseAsUTC) · 101 unmodified lines | ||
| 380 | 423 | |
| 381 | 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 | +} | |