From 76d967cf7cef073501d152cb4863af418e74cdbe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Wed, 17 Nov 2010 21:04:40 +0100
Subject: Added search parserialement.


diff --git a/Swiften/Elements/SearchPayload.h b/Swiften/Elements/SearchPayload.h
new file mode 100644
index 0000000..61b8547
--- /dev/null
+++ b/Swiften/Elements/SearchPayload.h
@@ -0,0 +1,94 @@
+/*
+ * 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/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Elements/Form.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+	/**
+	 * XEP-0055 search payload.
+	 */
+	class SearchPayload : public Payload {
+		public:
+			typedef boost::shared_ptr<SearchPayload> ref;
+
+			struct Item {
+				String first;
+				String last;
+				String nick;
+				String email;
+				JID jid;
+			};
+
+			SearchPayload() {}
+
+			Form::ref getForm() const { return form; }
+			void setForm(Form::ref f) { form = f; }
+
+			const boost::optional<String>& getInstructions() const {
+				return instructions;
+			}
+
+			const boost::optional<String>& getNick() const {
+				return nick;
+			}
+
+			const boost::optional<String>& getFirst() const {
+				return first;
+			}
+
+			const boost::optional<String>& getLast() const {
+				return last;
+			}
+
+			const boost::optional<String>& getEMail() const {
+				return email;
+			}
+
+			void setInstructions(const String& v) {
+				this->instructions = v;
+			}
+
+			void setNick(const String& v) {
+				this->nick = v;
+			}
+
+			void setFirst(const String& v) {
+				this->first = v;
+			}
+
+			void setLast(const String& v) {
+				this->last = v;
+			}
+
+			void setEMail(const String& v) {
+				this->email = v;
+			}
+
+			const std::vector<Item> getItems() const {
+				return items;
+			}
+
+			void addItem(const Item& item) {
+				items.push_back(item);
+			}
+
+		private:
+			Form::ref form;
+			boost::optional<String> instructions;
+			boost::optional<String> nick;
+			boost::optional<String> first;
+			boost::optional<String> last;
+			boost::optional<String> email;
+			std::vector<Item> items;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 41bba11..70dd81e 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -28,6 +28,7 @@
 #include "Swiften/Parser/PayloadParsers/FormParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/CommandParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/InBandRegistrationPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SearchPayloadParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/StreamInitiationParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/BytestreamsParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/IBBParserFactory.h"
@@ -64,6 +65,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new FormParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new CommandParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new InBandRegistrationPayloadParserFactory()));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new SearchPayloadParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new StreamInitiationParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new BytestreamsParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardUpdateParserFactory()));
diff --git a/Swiften/Parser/PayloadParsers/SearchPayloadParser.cpp b/Swiften/Parser/PayloadParsers/SearchPayloadParser.cpp
new file mode 100644
index 0000000..f2cf1dd
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SearchPayloadParser.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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/SearchPayloadParser.h"
+#include "Swiften/Parser/PayloadParsers/FormParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/FormParser.h"
+
+namespace Swift {
+
+SearchPayloadParser::SearchPayloadParser() : level(TopLevel), formParser(NULL)  {
+	formParserFactory = new FormParserFactory();
+}
+
+SearchPayloadParser::~SearchPayloadParser() {
+	delete formParserFactory;
+}
+
+void SearchPayloadParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+	if (level == TopLevel) {
+	}
+	else if (level == PayloadLevel) {
+		if (element == "x" && ns == "jabber:x:data") {
+			assert(!formParser);
+			formParser = dynamic_cast<FormParser*>(formParserFactory->createPayloadParser());
+		}
+		else if (element == "item") {
+			assert(!currentItem);
+			currentItem.reset(SearchPayload::Item());
+			currentItem->jid = JID(attributes.getAttribute("jid"));
+		}
+		else {
+			currentText.clear();
+		}
+	}
+	else if (level == ItemLevel && currentItem) {
+		currentText.clear();
+	}
+
+	if (formParser) {
+		formParser->handleStartElement(element, ns, attributes);
+	}
+
+	++level;
+}
+
+void SearchPayloadParser::handleEndElement(const String& element, const String& ns) {
+	--level;
+
+	if (formParser) {
+		formParser->handleEndElement(element, ns);
+	}
+
+	if (level == TopLevel) {
+	}
+	else if (level == PayloadLevel) {
+		if (formParser) {
+			getPayloadInternal()->setForm(formParser->getPayloadInternal());
+			delete formParser;
+			formParser = NULL;
+		}
+		else if (element == "item") {
+			assert(currentItem);
+			getPayloadInternal()->addItem(*currentItem);
+			currentItem.reset();
+		}
+		else if (element == "instructions") {
+			getPayloadInternal()->setInstructions(currentText);
+		}
+		else if (element == "nick") {
+			getPayloadInternal()->setNick(currentText);
+		}
+		else if (element == "first") {
+			getPayloadInternal()->setFirst(currentText);
+		}
+		else if (element == "last") {
+			getPayloadInternal()->setLast(currentText);
+		}
+		else if (element == "email") {
+			getPayloadInternal()->setEMail(currentText);
+		}
+	}
+	else if (level == ItemLevel && currentItem) {
+		if (element == "nick") {
+			currentItem->nick = currentText;
+		}
+		else if (element == "first") {
+			currentItem->first = currentText;
+		}
+		else if (element == "last") {
+			currentItem->last = currentText;
+		}
+		else if (element == "email") {
+			currentItem->email = currentText;
+		}
+	}
+}
+
+void SearchPayloadParser::handleCharacterData(const String& data) {
+	if (formParser) {
+		formParser->handleCharacterData(data);
+	}
+	else {
+		currentText += data;
+	}
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/SearchPayloadParser.h b/Swiften/Parser/PayloadParsers/SearchPayloadParser.h
new file mode 100644
index 0000000..55177b0
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SearchPayloadParser.h
@@ -0,0 +1,39 @@
+/*
+ * 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/SearchPayload.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+	class FormParserFactory;
+	class FormParser;
+
+	class SearchPayloadParser : public GenericPayloadParser<SearchPayload> {
+		public:
+			SearchPayloadParser();
+			~SearchPayloadParser();
+
+			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:
+			enum Level { 
+				TopLevel = 0, 
+				PayloadLevel = 1,
+				ItemLevel = 2,
+			};
+			int level;
+			FormParserFactory* formParserFactory;
+			FormParser* formParser;
+			String currentText;
+			boost::optional<SearchPayload::Item> currentItem;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/SearchPayloadParserFactory.h b/Swiften/Parser/PayloadParsers/SearchPayloadParserFactory.h
new file mode 100644
index 0000000..0f8a6c7
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SearchPayloadParserFactory.h
@@ -0,0 +1,17 @@
+/*
+ * 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/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SearchPayloadParser.h"
+
+namespace Swift {
+	class SearchPayloadParserFactory : public GenericPayloadParserFactory<SearchPayloadParser> {
+		public:
+			SearchPayloadParserFactory() : GenericPayloadParserFactory<SearchPayloadParser>("query", "jabber:iq:search") {}
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp
new file mode 100644
index 0000000..1d94c15
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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/SearchPayload.h"
+
+using namespace Swift;
+
+class SearchPayloadParserTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(SearchPayloadParserTest);
+		CPPUNIT_TEST(testParse_FormRequestResponse);
+		CPPUNIT_TEST(testParse_Results);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		void testParse_FormRequestResponse() {
+			PayloadsParserTester parser;
+
+			CPPUNIT_ASSERT(parser.parse(
+					"<query xmlns=\"jabber:iq:search\">"
+						"<instructions>Foo</instructions>"
+						"<first/>"
+						"<last/>"
+					"</query>"
+				));
+
+			SearchPayload::ref payload = parser.getPayload<SearchPayload>();
+			CPPUNIT_ASSERT_EQUAL(String("Foo"), *payload->getInstructions());
+			CPPUNIT_ASSERT(payload->getFirst());
+			CPPUNIT_ASSERT(payload->getLast());
+			CPPUNIT_ASSERT(!payload->getNick());
+		}
+
+		void testParse_Results() {
+			PayloadsParserTester parser;
+
+			CPPUNIT_ASSERT(parser.parse(
+					"<query xmlns=\"jabber:iq:search\">"
+						"<item jid=\"juliet@capulet.com\">"
+							"<first>Juliet</first>"
+							"<last>Capulet</last>"
+							"<nick>JuliC</nick>"
+							"<email>juliet@shakespeare.lit</email>"
+						"</item>"
+						"<item jid=\"tybalt@shakespeare.lit\">"
+							"<first>Tybalt</first>"
+							"<last>Capulet</last>"
+							"<nick>ty</nick>"
+							"<email>tybalt@shakespeare.lit</email>"
+						"</item>"
+					"</query>"
+				));
+
+			SearchPayload::ref payload = parser.getPayload<SearchPayload>();
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(payload->getItems().size()));
+			CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.com"), payload->getItems()[0].jid);
+			CPPUNIT_ASSERT_EQUAL(String("Juliet"), payload->getItems()[0].first);
+			CPPUNIT_ASSERT_EQUAL(String("Capulet"), payload->getItems()[0].last);
+			CPPUNIT_ASSERT_EQUAL(String("JuliC"), payload->getItems()[0].nick);
+			CPPUNIT_ASSERT_EQUAL(String("juliet@shakespeare.lit"), payload->getItems()[0].email);
+			CPPUNIT_ASSERT_EQUAL(JID("tybalt@shakespeare.lit"), payload->getItems()[1].jid);
+		}
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SearchPayloadParserTest);
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 92d5aa9..29398f0 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -30,6 +30,7 @@ sources = [
 		"PayloadParsers/IBBParser.cpp",
 		"PayloadParsers/CommandParser.cpp",
 		"PayloadParsers/InBandRegistrationPayloadParser.cpp",
+		"PayloadParsers/SearchPayloadParser.cpp",
 		"PayloadParsers/FullPayloadParserFactoryCollection.cpp",
 		"PayloadParsers/PriorityParser.cpp",
 		"PayloadParsers/PrivateStorageParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 7da329b..335dfa5 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -98,6 +98,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Serializer/PayloadSerializers/DelaySerializer.cpp",
 			"Serializer/PayloadSerializers/CommandSerializer.cpp",
 			"Serializer/PayloadSerializers/InBandRegistrationPayloadSerializer.cpp",
+			"Serializer/PayloadSerializers/SearchPayloadSerializer.cpp",
 			"Serializer/PayloadSerializers/FormSerializer.cpp",
 			"Serializer/PayloadSerializers/NicknameSerializer.cpp",
 			"Serializer/PresenceSerializer.cpp",
@@ -198,6 +199,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/RosterParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/IBBParserTest.cpp"),
+			File("Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp"),
@@ -238,6 +240,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp"),
+			File("Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/SecurityLabelsCatalogSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp"),
diff --git a/Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.cpp
new file mode 100644
index 0000000..5d71fd3
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLRawTextNode.h"
+#include "Swiften/Serializer/PayloadSerializers/FormSerializer.h"
+
+namespace Swift {
+
+SearchPayloadSerializer::SearchPayloadSerializer() {
+}
+
+String SearchPayloadSerializer::serializePayload(boost::shared_ptr<SearchPayload> searchPayload)	const {
+	XMLElement searchElement("query", "jabber:iq:search");
+
+	if (searchPayload->getInstructions()) {
+		searchElement.addNode(XMLElement::ref(new XMLElement("instructions", "", *searchPayload->getInstructions())));
+	}
+
+	if (searchPayload->getNick()) {
+		searchElement.addNode(XMLElement::ref(new XMLElement("nick", "", *searchPayload->getNick())));
+	}
+
+	if (searchPayload->getFirst()) {
+		searchElement.addNode(XMLElement::ref(new XMLElement("first", "", *searchPayload->getFirst())));
+	}
+
+	if (searchPayload->getLast()) {
+		searchElement.addNode(XMLElement::ref(new XMLElement("last", "", *searchPayload->getLast())));
+	}
+
+	if (searchPayload->getEMail()) {
+		searchElement.addNode(XMLElement::ref(new XMLElement("email", "", *searchPayload->getEMail())));
+	}
+
+	foreach(const SearchPayload::Item& item, searchPayload->getItems()) {
+		XMLElement::ref itemElement(new XMLElement("item"));
+		itemElement->setAttribute("jid", item.jid);
+		itemElement->addNode(XMLElement::ref(new XMLElement("first", "", item.first)));
+		itemElement->addNode(XMLElement::ref(new XMLElement("last", "", item.last)));
+		itemElement->addNode(XMLElement::ref(new XMLElement("nick", "", item.nick)));
+		itemElement->addNode(XMLElement::ref(new XMLElement("email", "", item.email)));
+
+		searchElement.addNode(itemElement);
+	}
+
+	if (Form::ref form = searchPayload->getForm()) {
+		searchElement.addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(FormSerializer().serialize(form))));
+	}
+
+	return searchElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.h
new file mode 100644
index 0000000..b64749b
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.h
@@ -0,0 +1,22 @@
+/*
+ * 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/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/SearchPayload.h"
+
+namespace Swift {
+	class PayloadSerializerCollection;
+
+	class SearchPayloadSerializer : public GenericPayloadSerializer<SearchPayload> {
+		public:
+			SearchPayloadSerializer();
+
+			virtual String serializePayload(boost::shared_ptr<SearchPayload>)  const;
+	};
+}
-- 
cgit v0.10.2-6-g49f6