From 234b140b5675c737859ed2457dcb07ad75a900ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Fri, 11 Sep 2009 22:08:45 +0200
Subject: Added bookmark storage payload & parser.


diff --git a/Swiften/Elements/Storage.h b/Swiften/Elements/Storage.h
new file mode 100644
index 0000000..00099c5
--- /dev/null
+++ b/Swiften/Elements/Storage.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <vector>
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+	class Storage : public Payload {
+		public:
+			struct Conference {
+				String name;
+				JID jid;
+				bool autoJoin;
+				String nick;
+				String password;
+			};
+
+			Storage() {
+			}
+
+			const std::vector<Conference>& getConferences() const {
+				return conferences;
+			}
+
+			void addConference(const Conference& conference) {
+				conferences.push_back(conference);
+			}
+
+		private:
+			std::vector<Conference> conferences;
+	};
+}
diff --git a/Swiften/Parser/AttributeMap.h b/Swiften/Parser/AttributeMap.h
index 6662b98..82c839a 100644
--- a/Swiften/Parser/AttributeMap.h
+++ b/Swiften/Parser/AttributeMap.h
@@ -19,6 +19,16 @@ namespace Swift {
 					return i->second;
 				}
 			}
+
+			bool getBoolAttribute(const String& attribute, bool defaultValue = false) const {
+				AttributeMap::const_iterator i = find(attribute);
+				if (i == end()) {
+					return defaultValue;
+				}
+				else {
+					return i->second == "true" || i->second == "1";
+				}
+			}
 	};
 }
 
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 8a7f468..f4a7b9d 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -11,6 +11,7 @@
 #include "Swiften/Parser/PayloadParsers/StatusShowParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/RosterParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/SoftwareVersionParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StorageParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h"
 #include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h"
@@ -29,6 +30,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new PriorityParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new ErrorParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new SoftwareVersionParserFactory()));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new StorageParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new RosterParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new DiscoInfoParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new ResourceBindParserFactory()));
diff --git a/Swiften/Parser/PayloadParsers/StorageParser.cpp b/Swiften/Parser/PayloadParsers/StorageParser.cpp
new file mode 100644
index 0000000..3eab15e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StorageParser.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/Parser/PayloadParsers/StorageParser.h"
+
+#include <cassert>
+
+namespace Swift {
+
+StorageParser::StorageParser() : level(TopLevel) {
+}
+
+void StorageParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) {
+	if (level == BookmarkLevel) {
+		if (element == "conference") {
+			assert(!conference);
+			conference = Storage::Conference();
+			conference->autoJoin = attributes.getBoolAttribute("autojoin", false);
+			conference->jid = JID(attributes.getAttribute("jid"));
+			conference->name = attributes.getAttribute("name");
+		}
+	}
+	else if (level == DetailLevel) {
+		currentText = "";
+	}
+	++level;
+}
+
+void StorageParser::handleEndElement(const String& element, const String&) {
+	--level;
+	if (level == BookmarkLevel) {
+		if (element == "conference") {
+			assert(conference);
+			getPayloadInternal()->addConference(*conference);
+			conference.reset();
+		}
+	}
+	else if (level == DetailLevel && conference) {
+		if (element == "nick") {
+			conference->nick = currentText;
+		}
+		else if (element == "password") {
+			conference->password = currentText;
+		}
+	}
+}
+
+void StorageParser::handleCharacterData(const String& data) {
+	currentText += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StorageParser.h b/Swiften/Parser/PayloadParsers/StorageParser.h
new file mode 100644
index 0000000..ad0ab4d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StorageParser.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/Storage.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+	class StorageParser : public GenericPayloadParser<Storage> {
+		public:
+			StorageParser();
+
+			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, 
+				BookmarkLevel = 1,
+				DetailLevel = 2
+			};
+			int level;
+			String currentText;
+			boost::optional<Storage::Conference> conference;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/StorageParserFactory.h b/Swiften/Parser/PayloadParsers/StorageParserFactory.h
new file mode 100644
index 0000000..147d178
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StorageParserFactory.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StorageParser.h"
+
+namespace Swift {
+	class StorageParserFactory : public GenericPayloadParserFactory<StorageParser> {
+		public:
+			StorageParserFactory() : GenericPayloadParserFactory<StorageParser>("storage", "storage:bookmarks") {}
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp
new file mode 100644
index 0000000..fa188b7
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp
@@ -0,0 +1,67 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/StorageParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class StorageParserTest : public CppUnit::TestFixture
+{
+		CPPUNIT_TEST_SUITE(StorageParserTest);
+		CPPUNIT_TEST(testParse_Conference);
+		CPPUNIT_TEST(testParse_MultipleConferences);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		StorageParserTest() {}
+
+		void testParse_Conference() {
+			StorageParser testling;
+			PayloadParserTester parser(&testling);
+
+			CPPUNIT_ASSERT(parser.parse(
+				"<storage xmlns='storage:bookmarks'>"
+					"<conference "
+							"name='Council of Oberon' "
+							"autojoin='true' jid='council@conference.underhill.org'>"
+						"<nick>Puck</nick>"
+						"<password>MyPass</password>"
+					"</conference>"
+				"</storage>"));
+
+			Storage* payload = dynamic_cast<Storage*>(testling.getPayload().get());
+			std::vector<Storage::Conference> conferences = payload->getConferences();
+			CPPUNIT_ASSERT_EQUAL(1U, conferences.size());
+			CPPUNIT_ASSERT_EQUAL(String("Council of Oberon"), conferences[0].name);
+			CPPUNIT_ASSERT_EQUAL(JID("council@conference.underhill.org"), conferences[0].jid);
+			CPPUNIT_ASSERT(conferences[0].autoJoin);
+			CPPUNIT_ASSERT_EQUAL(String("Puck"), conferences[0].nick);
+			CPPUNIT_ASSERT_EQUAL(String("MyPass"), conferences[0].password);
+		}
+
+		void testParse_MultipleConferences() {
+			StorageParser testling;
+			PayloadParserTester parser(&testling);
+
+			CPPUNIT_ASSERT(parser.parse(
+				"<storage xmlns='storage:bookmarks'>"
+					"<conference "
+							"name='Council of Oberon' "
+							"jid='council@conference.underhill.org' />"
+					"<conference "
+							"name='Tea party' "
+							"jid='teaparty@wonderland.lit' />"
+				"</storage>"));
+
+			Storage* payload = dynamic_cast<Storage*>(testling.getPayload().get());
+			std::vector<Storage::Conference> conferences = payload->getConferences();
+			CPPUNIT_ASSERT_EQUAL(2U, conferences.size());
+			CPPUNIT_ASSERT_EQUAL(String("Council of Oberon"), conferences[0].name);
+			CPPUNIT_ASSERT_EQUAL(JID("council@conference.underhill.org"), conferences[0].jid);
+			CPPUNIT_ASSERT_EQUAL(String("Tea party"), conferences[1].name);
+			CPPUNIT_ASSERT_EQUAL(JID("teaparty@wonderland.lit"), conferences[1].jid);
+		}
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StorageParserTest);
diff --git a/Swiften/Parser/UnitTest/AttributeMapTest.cpp b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
new file mode 100644
index 0000000..17bda95
--- /dev/null
+++ b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
@@ -0,0 +1,71 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/AttributeMap.h"
+
+using namespace Swift;
+
+class AttributeMapTest : public CppUnit::TestFixture
+{
+		CPPUNIT_TEST_SUITE(AttributeMapTest);
+		CPPUNIT_TEST(testGetBoolAttribute_True);
+		CPPUNIT_TEST(testGetBoolAttribute_1);
+		CPPUNIT_TEST(testGetBoolAttribute_False);
+		CPPUNIT_TEST(testGetBoolAttribute_0);
+		CPPUNIT_TEST(testGetBoolAttribute_Invalid);
+		CPPUNIT_TEST(testGetBoolAttribute_UnknownWithDefaultTrue);
+		CPPUNIT_TEST(testGetBoolAttribute_UnknownWithDefaultFalse);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		AttributeMapTest() {}
+
+		void testGetBoolAttribute_True() {
+			AttributeMap testling;
+			testling["foo"] = "true";
+
+			CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));
+		}
+
+		void testGetBoolAttribute_1() {
+			AttributeMap testling;
+			testling["foo"] = "1";
+
+			CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));
+		}
+
+		void testGetBoolAttribute_False() {
+			AttributeMap testling;
+			testling["foo"] = "false";
+
+			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
+		}
+
+		void testGetBoolAttribute_0() {
+			AttributeMap testling;
+			testling["foo"] = "0";
+
+			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
+		}
+
+		void testGetBoolAttribute_Invalid() {
+			AttributeMap testling;
+			testling["foo"] = "bla";
+
+			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
+		}
+
+		void testGetBoolAttribute_UnknownWithDefaultTrue() {
+			AttributeMap testling;
+
+			CPPUNIT_ASSERT(testling.getBoolAttribute("foo", true));
+		}
+
+		void testGetBoolAttribute_UnknownWithDefaultFalse() {
+			AttributeMap testling;
+
+			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", false));
+		}
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(AttributeMapTest);
diff --git a/Swiften/SConscript b/Swiften/SConscript
index abd9a44..6a1dde1 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -91,6 +91,7 @@ sources = [
 		"Parser/PayloadParsers/SecurityLabelParser.cpp",
 		"Parser/PayloadParsers/SecurityLabelsCatalogParser.cpp",
 		"Parser/PayloadParsers/SoftwareVersionParser.cpp",
+		"Parser/PayloadParsers/StorageParser.cpp",
 		"Parser/PayloadParsers/StatusParser.cpp",
 		"Parser/PayloadParsers/StatusShowParser.cpp",
 		"Parser/PayloadParsers/VCardParser.cpp",
@@ -226,7 +227,9 @@ env.Append(UNITTEST_SOURCES = [
 		File("Parser/PayloadParsers/UnitTest/StatusParserTest.cpp"),
 		File("Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp"),
 		File("Parser/PayloadParsers/UnitTest/VCardParserTest.cpp"),
+		File("Parser/PayloadParsers/UnitTest/StorageParserTest.cpp"),
 		File("Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp"),
+		File("Parser/UnitTest/AttributeMapTest.cpp"),
 		File("Parser/UnitTest/IQParserTest.cpp"),
 		File("Parser/UnitTest/MessageParserTest.cpp"),
 		File("Parser/UnitTest/PayloadParserFactoryCollectionTest.cpp"),
-- 
cgit v0.10.2-6-g49f6