diff --git a/Source/Core/BaseXMLParser.cpp b/Source/Core/BaseXMLParser.cpp index 0d792df48..21b00d25e 100644 --- a/Source/Core/BaseXMLParser.cpp +++ b/Source/Core/BaseXMLParser.cpp @@ -476,6 +476,7 @@ bool BaseXMLParser::FindString(const char* string, String& data, bool escape_bra { int index = 0; bool in_brackets = false; + bool in_string = false; char previous = 0; while (string[index]) @@ -493,7 +494,7 @@ bool BaseXMLParser::FindString(const char* string, String& data, bool escape_bra if(escape_brackets) { - const char* error_str = XMLParseTools::ParseDataBrackets(in_brackets, c, previous); + const char* error_str = XMLParseTools::ParseDataBrackets(in_brackets, in_string, c, previous); if (error_str) { Log::Message(Log::LT_WARNING, "XML parse error. %s", error_str); diff --git a/Source/Core/DataViewDefault.cpp b/Source/Core/DataViewDefault.cpp index 5a86e707e..70599317a 100644 --- a/Source/Core/DataViewDefault.cpp +++ b/Source/Core/DataViewDefault.cpp @@ -29,6 +29,7 @@ #include "DataViewDefault.h" #include "DataExpression.h" #include "DataModel.h" +#include "XMLParseTools.h" #include "../../Include/RmlUi/Core/Core.h" #include "../../Include/RmlUi/Core/DataVariable.h" #include "../../Include/RmlUi/Core/Element.h" @@ -328,35 +329,56 @@ bool DataViewText::Initialize(DataModel& model, Element* element, const String& DataExpressionInterface expression_interface(&model, element); - size_t previous_close_brackets = 0; size_t begin_brackets = 0; - while ((begin_brackets = in_text.find("{{", begin_brackets)) != String::npos) - { - text.insert(text.end(), in_text.begin() + previous_close_brackets, in_text.begin() + begin_brackets); + size_t cur = 0; + char previous = 0; + bool was_in_brackets = false; + bool in_brackets = false; + bool in_string = false; - const size_t begin_name = begin_brackets + 2; - const size_t end_name = in_text.find("}}", begin_name); + for(char c : in_text) { + was_in_brackets = in_brackets; - if (end_name == String::npos) + const char* error_str = XMLParseTools::ParseDataBrackets(in_brackets, in_string, c, previous); + if (error_str) + { + Log::Message(Log::LT_WARNING, "Failed to parse data view text '%s'. %s", in_text.c_str(), error_str); return false; + } + + if (!was_in_brackets && in_brackets) + { + begin_brackets = cur; + } + else if (was_in_brackets && !in_brackets) + { + DataEntry entry; + entry.index = text.size(); + entry.data_expression = MakeUnique(String(in_text.begin() + begin_brackets + 1, in_text.begin() + cur - 2)); - DataEntry entry; - entry.index = text.size(); - entry.data_expression = MakeUnique(String(in_text.begin() + begin_name, in_text.begin() + end_name)); + if (entry.data_expression->Parse(expression_interface, false)) + data_entries.push_back(std::move(entry)); - if (entry.data_expression->Parse(expression_interface, false)) - data_entries.push_back(std::move(entry)); + // Reset char so that it won't appended to the output + c = 0; + } + else if (!in_brackets && previous) + { + text.push_back(previous); + } - previous_close_brackets = end_name + 2; - begin_brackets = previous_close_brackets; + cur++; + previous = c; + } + + if (!in_brackets && previous) + { + text.push_back(previous); } if (data_entries.empty()) return false; - if (previous_close_brackets < in_text.size()) - text.insert(text.end(), in_text.begin() + previous_close_brackets, in_text.end()); - return true; } diff --git a/Source/Core/Factory.cpp b/Source/Core/Factory.cpp index 22484f6cf..bb2e437d7 100644 --- a/Source/Core/Factory.cpp +++ b/Source/Core/Factory.cpp @@ -400,10 +400,11 @@ bool Factory::InstanceElementText(Element* parent, const String& in_text) bool has_data_expression = false; bool inside_brackets = false; + bool inside_string = false; char previous = 0; for (const char c : text) { - const char* error_str = XMLParseTools::ParseDataBrackets(inside_brackets, c, previous); + const char* error_str = XMLParseTools::ParseDataBrackets(inside_brackets, inside_string, c, previous); if (error_str) { Log::Message(Log::LT_WARNING, "Failed to instance text element '%s'. %s", text.c_str(), error_str); diff --git a/Source/Core/XMLParseTools.cpp b/Source/Core/XMLParseTools.cpp index d47189854..58f0ace79 100644 --- a/Source/Core/XMLParseTools.cpp +++ b/Source/Core/XMLParseTools.cpp @@ -153,24 +153,30 @@ Element* XMLParseTools::ParseTemplate(Element* element, const String& template_n return parse_template->ParseTemplate(element); } -const char* XMLParseTools::ParseDataBrackets(bool& inside_brackets, char c, char previous) +const char* XMLParseTools::ParseDataBrackets(bool& inside_brackets, bool& inside_string, char c, char previous) { if (inside_brackets) { - if (c == '}' && previous == '}') - inside_brackets = false; + if (c == '\'') + inside_string = !inside_string; - else if (c == '{' && previous == '{') - return "Nested double curly brackets are illegal."; + if(!inside_string) + { + if (c == '}' && previous == '}') + inside_brackets = false; + + else if (c == '{' && previous == '{') + return "Nested double curly brackets are illegal."; - else if (previous == '}' && c != '}') - return "Single closing curly bracket encountered, use double curly brackets to close an expression."; + else if (previous == '}' && c != '}') + return "Single closing curly bracket encountered, use double curly brackets to close an expression."; - else if (previous == '/' && c == '>') - return "Closing double curly brackets not found, XML end node encountered first."; + else if (previous == '/' && c == '>') + return "Closing double curly brackets not found, XML end node encountered first."; - else if (previous == '<' && c == '/') - return "Closing double curly brackets not found, XML end node encountered first."; + else if (previous == '<' && c == '/') + return "Closing double curly brackets not found, XML end node encountered first."; + } } else { diff --git a/Source/Core/XMLParseTools.h b/Source/Core/XMLParseTools.h index 1887658ec..747fcbc14 100644 --- a/Source/Core/XMLParseTools.h +++ b/Source/Core/XMLParseTools.h @@ -64,8 +64,9 @@ class XMLParseTools /// Determine the presence of data expression brackets inside XML data. /// Call this for each iteration through the data string. /// 'inside_brackets' should be initialized to false. + /// 'inside_string' should be initialized to false. /// Returns nullptr on success, or an error string on failure. - static const char* ParseDataBrackets(bool& inside_brackets, char c, char previous); + static const char* ParseDataBrackets(bool& inside_brackets, bool& inside_string, char c, char previous); }; } // namespace Rml diff --git a/Tests/Source/UnitTests/DataBinding.cpp b/Tests/Source/UnitTests/DataBinding.cpp index 5b242fed3..cd7824229 100644 --- a/Tests/Source/UnitTests/DataBinding.cpp +++ b/Tests/Source/UnitTests/DataBinding.cpp @@ -114,6 +114,38 @@ static const String document_rml = R"( )"; +static const String inside_string_rml = R"( + + + Test + + + + + + +
+ +

{{ i0 }}

+

{{ 'i0' }}

+

{{ 'i{}23' }}

+

before {{ 'i{{test}}23' }} test

+

a {{ 'i' }} b {{ 'j' }} c

+ +
+ +
+)"; struct StringWrap { @@ -393,3 +425,27 @@ TEST_CASE("databinding") TestsShell::ShutdownShell(); } + +TEST_CASE("databinding.inside_string") +{ + Context* context = TestsShell::GetContext(); + REQUIRE(context); + + REQUIRE(InitializeDataBindings(context)); + + ElementDocument* document = context->LoadDocumentFromMemory(inside_string_rml); + REQUIRE(document); + document->Show(); + + context->Update(); + context->Render(); + + TestsShell::RenderLoop(); + + CHECK(document->QuerySelector("p:nth-child(4)")->GetInnerRML() == "before i{{test}}23 test"); + CHECK(document->QuerySelector("p:nth-child(5)")->GetInnerRML() == "a i b j c"); + + document->Close(); + + TestsShell::ShutdownShell(); +} \ No newline at end of file