diff options
-rw-r--r-- | src/core/parser/stack/Stack.cpp | 57 | ||||
-rw-r--r-- | src/core/parser/stack/Stack.hpp | 7 | ||||
-rw-r--r-- | test/core/parser/stack/StackTest.cpp | 115 |
3 files changed, 140 insertions, 39 deletions
diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index b98cddb..07f7d8c 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -74,12 +74,12 @@ void HandlerInfo::fieldStart(bool isDefault, bool isImplicit, bool isValid) inDefaultField = isDefault || isImplicit; inImplicitDefaultField = isImplicit; inValidField = isValid; - hadDefaultField = hadDefaultField || inDefaultField; fieldIdx++; } void HandlerInfo::fieldEnd() { + hadDefaultField = hadDefaultField || inDefaultField; inField = false; inDefaultField = false; inImplicitDefaultField = false; @@ -269,6 +269,22 @@ void Stack::endCurrentHandler() } } +void Stack::endOverdueHandlers() +{ + if (!stack.empty()) { + // Fetch the handler info for the current top-level element + HandlerInfo &info = stack.back(); + + // Abort if this handler currently is inside a field + if (info.inField || !info.hadDefaultField) { + return; + } + + // Otherwise end the current handler + endCurrentHandler(); + } +} + bool Stack::ensureHandlerIsInField() { // If the current handler is not in a field (and actually has a handler) @@ -308,6 +324,10 @@ Logger &Stack::logger() { return ctx.getLogger(); } void Stack::command(const Variant &name, const Variant::mapType &args) { + // End handlers that already had a default field and are currently not + // active. + endOverdueHandlers(); + // Make sure the given identifier is valid (preventing "*" from being // malicously passed to this function) if (!Utils::isNamespacedIdentifier(name.asString())) { @@ -396,6 +416,10 @@ void Stack::command(const Variant &name, const Variant::mapType &args) void Stack::data(const Variant &data) { + // End handlers that already had a default field and are currently not + // active. + endOverdueHandlers(); + while (true) { // Check whether there is any command the data can be sent to if (stack.empty()) { @@ -414,7 +438,11 @@ void Stack::data(const Variant &data) // If this field should not get any data, log an error and do not call // the "data" handler if (!info.inValidField) { - logger().error("Did not expect any data here", data); + // If the "hadDefaultField" flag is set, we already issued an error + // message + if (!info.hadDefaultField) { + logger().error("Did not expect any data here", data); + } } if (handlersValid() && info.inValidField) { @@ -472,13 +500,24 @@ void Stack::fieldStart(bool isDefault) return; } + // If the handler already had a default field we cannot start a new field + // (the default field always is the last field) -- mark the command as + // invalid + if (info.hadDefaultField) { + logger().error( + std::string("Got field start, but command \"") + + currentCommandName() + + std::string("\" does not have any more fields")); + } + // Copy the isDefault flag to a local variable, the fieldStart method will // write into this variable bool defaultField = isDefault; // Do not call the "fieldStart" function if we're in an invalid subtree + // or the handler already had a default field bool valid = false; - if (handlersValid()) { + if (handlersValid() && !info.hadDefaultField) { try { valid = info.handler->fieldStart(defaultField, info.fieldIdx); } @@ -499,11 +538,6 @@ void Stack::fieldStart(bool isDefault) void Stack::fieldEnd() { - // Check whether there is any command the data can be sent to - if (stack.empty()) { - throw LoggableException("No command, but got end of field."); - } - // Unroll the stack until the next explicitly open field while (!stack.empty()) { HandlerInfo &info = currentInfo(); @@ -524,7 +558,7 @@ void Stack::fieldEnd() // Only continue if the current handler stack is in a valid state, do not // call the fieldEnd function if something went wrong before - if (handlersValid()) { + if (handlersValid() && !info.hadDefaultField) { try { info.handler->fieldEnd(); } @@ -535,11 +569,6 @@ void Stack::fieldEnd() // This command no longer is in a field info.fieldEnd(); - - // As soon as this command had a default field, remove it from the stack - if (info.hadDefaultField) { - endCurrentHandler(); - } } void Stack::annotationStart(const Variant &className, const Variant &args) diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp index 76eefd9..b67ce82 100644 --- a/src/core/parser/stack/Stack.hpp +++ b/src/core/parser/stack/Stack.hpp @@ -220,6 +220,13 @@ private: HandlerInfo &lastInfo(); /** + * Ends all handlers that currently are not inside a field and already had + * a default field. This method is called whenever the data() and command() + * events are reached. + */ + void endOverdueHandlers(); + + /** * Ends the current handler and removes the corresponding element from the * stack. */ diff --git a/test/core/parser/stack/StackTest.cpp b/test/core/parser/stack/StackTest.cpp index 59fdd59..e25f487 100644 --- a/test/core/parser/stack/StackTest.cpp +++ b/test/core/parser/stack/StackTest.cpp @@ -230,30 +230,34 @@ TEST(Stack, basicTest) EXPECT_EQ(&States::BodyChildren, &s.currentState()); s.fieldEnd(); - tracker.expect(3, 1, 3, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(3, 0, 3, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc s.fieldEnd(); - EXPECT_EQ("document", s.currentCommandName()); - EXPECT_EQ(&States::Document, &s.currentState()); - tracker.expect(3, 2, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); + tracker.expect(3, 1, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc s.command("body", {}); + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); + tracker.expect(4, 2, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc s.fieldStart(true); s.data("test3"); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); s.fieldEnd(); - tracker.expect(4, 3, 4, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(4, 2, 4, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("document", s.currentCommandName()); - EXPECT_EQ(&States::Document, &s.currentState()); + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); s.fieldEnd(); - tracker.expect(4, 4, 4, 4, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(4, 3, 4, 4, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("", s.currentCommandName()); - EXPECT_EQ(&States::None, &s.currentState()); + EXPECT_EQ("document", s.currentCommandName()); + EXPECT_EQ(&States::Document, &s.currentState()); } + tracker.expect(4, 4, 4, 4, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc ASSERT_FALSE(logger.hasError()); } @@ -273,9 +277,13 @@ TEST(Stack, errorInvalidCommands) s.fieldEnd(); s.fieldEnd(); s.fieldEnd(); + + logger.reset(); + s.fieldEnd(); + ASSERT_TRUE(logger.hasError()); + + EXPECT_THROW(s.data("test"), LoggableException); EXPECT_EQ(&States::None, &s.currentState()); - ASSERT_THROW(s.fieldEnd(), LoggableException); - ASSERT_THROW(s.data("test"), LoggableException); } TEST(Stack, validation) @@ -304,27 +312,34 @@ TEST(Stack, validation) TEST(Stack, invalidCommandName) { - Stack s{env.context, States::AnyHandlers}; tracker.reset(); logger.reset(); + Stack s{env.context, States::AnyHandlers}; s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc s.fieldStart(true); s.fieldEnd(); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc s.command("a_", {}); + tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc s.fieldStart(true); s.fieldEnd(); - tracker.expect(2, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 1, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc s.command("a_:b", {}); + tracker.expect(3, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc s.fieldStart(true); s.fieldEnd(); - tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(3, 2, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc ASSERT_THROW(s.command("_a", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_THROW(s.command("a:", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_THROW(s.command("a:_b", {}), LoggableException); tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } @@ -375,8 +390,9 @@ TEST(Stack, multipleFields) EXPECT_EQ("test3", tracker.dataData); s.fieldEnd(); - tracker.expect(1, 1, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(1, 0, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc } + tracker.expect(1, 1, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc ASSERT_FALSE(logger.hasError()); } @@ -413,8 +429,8 @@ TEST(Stack, implicitDefaultFieldOnNewCommandWithExplicitDefaultField) ASSERT_EQ("b", s.currentCommandName()); s.fieldStart(true); s.fieldEnd(); - tracker.expect(2, 1, 2, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); + tracker.expect(2, 0, 2, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("b", s.currentCommandName()); } tracker.expect(2, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc ASSERT_FALSE(logger.hasError()); @@ -454,8 +470,8 @@ TEST(Stack, noImplicitDefaultFieldIfDefaultFieldGiven) tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc ASSERT_EQ("a", s.currentCommandName()); s.fieldEnd(); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("", s.currentCommandName()); + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("a", s.currentCommandName()); s.command("b", {}); tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc @@ -530,7 +546,7 @@ TEST(Stack, autoImplicitFieldEnd) s.command("e", {}); s.fieldStart(true); s.fieldEnd(); - tracker.expect(5, 1, 5, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(5, 0, 5, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } tracker.expect(5, 5, 5, 5, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc ASSERT_FALSE(logger.hasError()); @@ -547,7 +563,7 @@ TEST(Stack, invalidDefaultField) tracker.fieldStartResult = false; s.fieldStart(true); s.fieldEnd(); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc ASSERT_FALSE(logger.hasError()); @@ -567,7 +583,7 @@ TEST(Stack, errorInvalidDefaultFieldData) s.data("test"); ASSERT_TRUE(logger.hasError()); s.fieldEnd(); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } @@ -674,8 +690,57 @@ TEST(Stack, fieldEndWhenImplicitDefaultFieldOpen) s.command("b", {}); s.data("test"); s.fieldEnd(); - tracker.expect(2, 2, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 1, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(2, 2, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); +} + +TEST(Stack, fieldAfterDefaultField) +{ + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldStart(true); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.command("b", {}); + tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldStart(false); + tracker.expect(2, 0, 2, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.data("f1"); + tracker.expect(2, 0, 2, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(2, 0, 2, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.fieldStartSetIsDefault = true; + + s.fieldStart(false); + tracker.fieldStartSetIsDefault = false; + tracker.expect(2, 0, 3, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + s.data("f2"); + tracker.expect(2, 0, 3, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + + ASSERT_FALSE(logger.hasError()); + s.fieldStart(false); + ASSERT_TRUE(logger.hasError()); + logger.reset(); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + s.data("f3"); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldEnd(); + tracker.expect(2, 1, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc } + tracker.expect(2, 2, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc ASSERT_FALSE(logger.hasError()); } |