diff options
Diffstat (limited to 'Swiften/Parser')
8 files changed, 311 insertions, 1 deletions
diff --git a/Swiften/Parser/PayloadParsers/CommandParser.cpp b/Swiften/Parser/PayloadParsers/CommandParser.cpp new file mode 100644 index 0000000..4e80829 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/CommandParser.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Parser/PayloadParsers/CommandParser.h" +#include "Swiften/Parser/PayloadParsers/FormParserFactory.h" +#include "Swiften/Parser/PayloadParsers/FormParser.h" + +namespace Swift { + +CommandParser::CommandParser() : level_(TopLevel), inNote_(false), inActions_(false), formParser_(0) { + formParserFactory_ = new FormParserFactory(); +} + +CommandParser::~CommandParser() { + delete formParserFactory_; +} + +void CommandParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) { + ++level_; + if (level_ == PayloadLevel) { + boost::optional<Command::Action> action = parseAction(attributes.getAttribute("action")); + if (action) { + getPayloadInternal()->setAction(*action); + } + + String status = attributes.getAttribute("status"); + if (status == "executing") { + getPayloadInternal()->setStatus(Command::Executing); + } + else if (status == "completed") { + getPayloadInternal()->setStatus(Command::Completed); + } + else if (status == "canceled") { + getPayloadInternal()->setStatus(Command::Canceled); + } + + getPayloadInternal()->setNode(attributes.getAttribute("node")); + getPayloadInternal()->setSessionID(attributes.getAttribute("sessionid")); + } + else if (level_ == FormOrNoteOrActionsLevel) { + assert(!formParser_); + if (formParserFactory_->canParse(element, ns, attributes)) { + formParser_ = dynamic_cast<FormParser*>(formParserFactory_->createPayloadParser()); + assert(formParser_); + } + else if (element == "note") { + inNote_ = true; + currentText_.clear(); + String noteType = attributes.getAttribute("type"); + if (noteType == "info") { + noteType_ = Command::Note::Info; + } + else if (noteType == "warn") { + noteType_ = Command::Note::Warn; + } + else if (noteType == "error") { + noteType_ = Command::Note::Error; + } + else { + noteType_ = Command::Note::Info; + } + } + else if (element == "actions") { + inActions_ = true; + boost::optional<Command::Action> action = parseAction(attributes.getAttribute("execute")); + if (action) { + getPayloadInternal()->setExecuteAction(*action); + } + } + } + else if (level_ == ActionsActionLevel) { + } + + if (formParser_) { + formParser_->handleStartElement(element, ns, attributes); + } +} + +void CommandParser::handleEndElement(const String& element, const String& ns) { + if (formParser_) { + formParser_->handleEndElement(element, ns); + } + + if (level_ == FormOrNoteOrActionsLevel) { + if (formParser_) { + Form::ref form = Form::cast(formParser_->getPayload()); + assert(form); + getPayloadInternal()->setForm(form); + delete formParser_; + formParser_ = 0; + } + else if (inNote_) { + inNote_ = false; + getPayloadInternal()->addNote(Command::Note(currentText_, noteType_)); + } + else if (inActions_) { + inActions_ = false; + } + } + else if (level_ == ActionsActionLevel && inActions_) { + boost::optional<Command::Action> action = parseAction(element); + if (action) { + getPayloadInternal()->addAvailableAction(*action); + } + } + --level_; +} + +void CommandParser::handleCharacterData(const String& data) { + if (formParser_) { + formParser_->handleCharacterData(data); + } + else { + currentText_ += data; + } +} + +boost::optional<Command::Action> CommandParser::parseAction(const String& action) { + if (action == "execute") { + return Command::Execute; + } + else if (action == "cancel") { + return Command::Cancel; + } + else if (action == "prev") { + return Command::Prev; + } + else if (action == "next") { + return Command::Next; + } + else if (action == "complete") { + return Command::Complete; + } + return boost::optional<Command::Action>(); +} + +} diff --git a/Swiften/Parser/PayloadParsers/CommandParser.h b/Swiften/Parser/PayloadParsers/CommandParser.h new file mode 100644 index 0000000..a682a80 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/CommandParser.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/optional.hpp> + +#include "Swiften/Elements/Command.h" +#include "Swiften/Parser/GenericPayloadParser.h" + +namespace Swift { + class FormParserFactory; + class FormParser; + + class CommandParser : public GenericPayloadParser<Command> { + public: + CommandParser(); + ~CommandParser(); + + virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes); + virtual void handleEndElement(const String& element, const String&); + virtual void handleCharacterData(const String& data); + + private: + static boost::optional<Command::Action> parseAction(const String& action); + + private: + enum Level { + TopLevel = 0, + PayloadLevel = 1, + FormOrNoteOrActionsLevel = 2, + ActionsActionLevel = 3 + }; + int level_; + bool inNote_; + bool inActions_; + Command::Note::Type noteType_; + FormParserFactory* formParserFactory_; + FormParser* formParser_; + String currentText_; + }; +} diff --git a/Swiften/Parser/PayloadParsers/CommandParserFactory.h b/Swiften/Parser/PayloadParsers/CommandParserFactory.h new file mode 100644 index 0000000..da2f484 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/CommandParserFactory.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Parser/PayloadParserFactory.h" +#include "Swiften/Parser/PayloadParsers/CommandParser.h" + +namespace Swift { + class PayloadParserFactoryCollection; + + class CommandParserFactory : public PayloadParserFactory { + public: + CommandParserFactory() { + } + + virtual bool canParse(const String& element, const String& ns, const AttributeMap&) const { + return ns == "http://jabber.org/protocol/commands" && element == "command"; + } + + virtual PayloadParser* createPayloadParser() { + return new CommandParser(); + } + + }; +} diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp index b115b10..ce6c9f8 100644 --- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp +++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp @@ -25,6 +25,7 @@ #include "Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h" #include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h" #include "Swiften/Parser/PayloadParsers/FormParserFactory.h" +#include "Swiften/Parser/PayloadParsers/CommandParserFactory.h" #include "Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h" #include "Swiften/Parser/PayloadParsers/VCardParserFactory.h" #include "Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h" @@ -53,6 +54,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() { factories_.push_back(shared_ptr<PayloadParserFactory>(new SecurityLabelParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new SecurityLabelsCatalogParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new FormParserFactory())); + factories_.push_back(shared_ptr<PayloadParserFactory>(new CommandParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardUpdateParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new PrivateStorageParserFactory(this))); diff --git a/Swiften/Parser/PayloadParsers/UnitTest/CommandParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/CommandParserTest.cpp new file mode 100644 index 0000000..5f11718 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/UnitTest/CommandParserTest.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h" +#include "Swiften/Elements/Command.h" + +using namespace Swift; + +class CommandParserTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(CommandParserTest); + CPPUNIT_TEST(testParse); + CPPUNIT_TEST(testParse_Result); + CPPUNIT_TEST(testParse_Form); + CPPUNIT_TEST_SUITE_END(); + + public: + void testParse() { + PayloadsParserTester parser; + + CPPUNIT_ASSERT(parser.parse( + "<command xmlns='http://jabber.org/protocol/commands' node='list' action='prev' sessionid='myid'/>" + )); + + Command::ref payload = parser.getPayload<Command>(); + CPPUNIT_ASSERT_EQUAL(Command::Prev, payload->getAction()); + CPPUNIT_ASSERT_EQUAL(String("list"), payload->getNode()); + CPPUNIT_ASSERT_EQUAL(String("myid"), payload->getSessionID()); + } + + void testParse_Result() { + PayloadsParserTester parser; + + CPPUNIT_ASSERT(parser.parse( + "<command xmlns='http://jabber.org/protocol/commands' node='config' status='completed' sessionid='myid'>" + "<note type='warn'>Service 'httpd' has been configured.</note>" + "<note type='error'>I lied.</note>" + "<actions execute='next'>" + "<prev/>" + "<next/>" + "</actions>" + "</command>" + )); + + Command::ref payload = parser.getPayload<Command>(); + CPPUNIT_ASSERT_EQUAL(Command::Completed, payload->getStatus()); + std::vector<Command::Note> notes = payload->getNotes(); + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(notes.size())); + CPPUNIT_ASSERT_EQUAL(Command::Note::Warn, notes[0].type); + CPPUNIT_ASSERT_EQUAL(String("Service 'httpd' has been configured."), notes[0].note); + CPPUNIT_ASSERT_EQUAL(Command::Note::Error, notes[1].type); + CPPUNIT_ASSERT_EQUAL(String("I lied."), notes[1].note); + std::vector<Command::Action> actions = payload->getAvailableActions(); + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(actions.size())); + CPPUNIT_ASSERT_EQUAL(Command::Prev, actions[0]); + CPPUNIT_ASSERT_EQUAL(Command::Next, actions[1]); + CPPUNIT_ASSERT_EQUAL(Command::Next, payload->getExecuteAction()); + } + + void testParse_Form() { + PayloadsParserTester parser; + + CPPUNIT_ASSERT(parser.parse( + "<command xmlns='http://jabber.org/protocol/commands' node='config' status='completed'>" + "<x type=\"result\" xmlns=\"jabber:x:data\">" + "<title>Bot Configuration</title>" + "<instructions>Hello!</instructions>" + "<instructions>Fill out this form to configure your new bot!</instructions>" + "</x>" + "</command>" + )); + + Command::ref payload = parser.getPayload<Command>(); + Form::ref form = payload->getForm(); + CPPUNIT_ASSERT_EQUAL(String("Bot Configuration"), form->getTitle()); + CPPUNIT_ASSERT_EQUAL(String("Hello!\nFill out this form to configure your new bot!"), form->getInstructions()); + CPPUNIT_ASSERT_EQUAL(Form::ResultType, form->getType()); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(CommandParserTest); diff --git a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp index e7d80e3..0d6e85e 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp @@ -7,8 +7,8 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include "Swiften/Parser/PayloadParsers/FormParser.h" #include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h" +#include "Swiften/Elements/Form.h" using namespace Swift; diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h b/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h index 9ce0ac4..ee64181 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h +++ b/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h @@ -12,6 +12,8 @@ #include "Swiften/Parser/XMLParser.h" #include "Swiften/Parser/XMLParserClient.h" #include "Swiften/Parser/PlatformXMLParserFactory.h" +#include "Swiften/Elements/Payload.h" +#include "Swiften/Parser/PayloadParser.h" namespace Swift { class PayloadsParserTester : public XMLParserClient { @@ -52,6 +54,11 @@ namespace Swift { return payloadParser->getPayload(); } + template<typename T> + boost::shared_ptr<T> getPayload() const { + return boost::dynamic_pointer_cast<T>(payloadParser->getPayload()); + } + private: XMLParser* xmlParser; FullPayloadParserFactoryCollection factories; diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript index 1826432..4efd343 100644 --- a/Swiften/Parser/SConscript +++ b/Swiften/Parser/SConscript @@ -24,6 +24,7 @@ sources = [ "PayloadParsers/DiscoItemsParser.cpp", "PayloadParsers/ErrorParser.cpp", "PayloadParsers/FormParser.cpp", + "PayloadParsers/CommandParser.cpp", "PayloadParsers/FullPayloadParserFactoryCollection.cpp", "PayloadParsers/PriorityParser.cpp", "PayloadParsers/PrivateStorageParser.cpp", |