From 92af1a97f318f7f623df3183e90e7e828fa2eeb9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Mon, 11 Apr 2011 22:15:20 +0200
Subject: Make parser infrastructure parser aware.

Resolves: #492

diff --git a/Swiften/Parser/Attribute.h b/Swiften/Parser/Attribute.h
new file mode 100644
index 0000000..f1f9a83
--- /dev/null
+++ b/Swiften/Parser/Attribute.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <string>
+
+namespace Swift {
+	class Attribute {
+		public:
+			Attribute(const std::string& name, const std::string& ns) : name(name), ns(ns) {
+			}
+
+			const std::string& getName() const {
+				return name;
+			}
+
+			const std::string& getNamespace() const {
+				return ns;
+			}
+
+			bool operator==(const Attribute& o) const {
+				return o.name == name && o.ns == ns;
+			}
+
+		private:
+			std::string name;
+			std::string ns;
+	};
+}
diff --git a/Swiften/Parser/AttributeMap.cpp b/Swiften/Parser/AttributeMap.cpp
new file mode 100644
index 0000000..1aeaf99
--- /dev/null
+++ b/Swiften/Parser/AttributeMap.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Parser/AttributeMap.h>
+
+#include <algorithm>
+#include <boost/optional.hpp>
+
+using namespace Swift;
+
+namespace {
+	struct AttributeIs {
+		AttributeIs(const Attribute& attribute) : attribute(attribute) {
+		}
+
+		bool operator()(const AttributeMap::Entry& o) const {
+			return o.getAttribute() == attribute;
+		}
+
+		Attribute attribute;
+	};
+}
+
+AttributeMap::AttributeMap() {
+}
+
+std::string AttributeMap::getAttribute(const std::string& attribute, const std::string& ns) const {
+	AttributeValueMap::const_iterator i = std::find_if(attributes.begin(), attributes.end(), AttributeIs(Attribute(attribute, ns)));
+	if (i == attributes.end()) {
+		return "";
+	}
+	else {
+		return i->getValue();
+	}
+}
+
+bool AttributeMap::getBoolAttribute(const std::string& attribute, bool defaultValue) const {
+	AttributeValueMap::const_iterator i = std::find_if(attributes.begin(), attributes.end(), AttributeIs(Attribute(attribute, "")));
+	if (i == attributes.end()) {
+		return defaultValue;
+	}
+	else {
+		return i->getValue() == "true" || i->getValue() == "1";
+	}
+}
+
+boost::optional<std::string> AttributeMap::getAttributeValue(const std::string& attribute) const {
+	AttributeValueMap::const_iterator i = std::find_if(attributes.begin(), attributes.end(), AttributeIs(Attribute(attribute, "")));
+	if (i == attributes.end()) {
+		return boost::optional<std::string>();
+	}
+	else {
+		return i->getValue();
+	}
+}
+
+void AttributeMap::addAttribute(const std::string& name, const std::string& ns, const std::string& value) {
+	attributes.push_back(Entry(Attribute(name, ns), value));
+}
diff --git a/Swiften/Parser/AttributeMap.h b/Swiften/Parser/AttributeMap.h
index c8b287b..31df606 100644
--- a/Swiften/Parser/AttributeMap.h
+++ b/Swiften/Parser/AttributeMap.h
@@ -4,38 +4,50 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#ifndef ATTRIBUTEMAP_H
-#define ATTRIBUTEMAP_H
+#pragma once
 
+#include <vector>
+#include <string>
 #include <map>
+#include <boost/optional/optional_fwd.hpp>
 
-#include <string>
+#include <Swiften/Parser/Attribute.h>
 
 namespace Swift {
-	class AttributeMap : public std::map<std::string,std::string> {
+	class AttributeMap {
 		public:
-			AttributeMap() {}
-
-			std::string getAttribute(const std::string& attribute) const {
-				AttributeMap::const_iterator i = find(attribute);
-				if (i == end()) {
-					return "";
-				}
-				else {
-					return i->second;
-				}
-			}
+			class Entry {
+				public:
+					Entry(const Attribute& attribute, const std::string& value) : attribute(attribute), value(value) {
+					}
+
+					const Attribute& getAttribute() const {
+						return attribute;
+					}
+
+					const std::string& getValue() const {
+						return value;
+					}
+
+				private:
+					Attribute attribute;
+					std::string value;
+			};
 
-			bool getBoolAttribute(const std::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";
-				}
+			AttributeMap();
+
+			std::string getAttribute(const std::string& attribute, const std::string& ns = "") const;
+			bool getBoolAttribute(const std::string& attribute, bool defaultValue = false) const;
+			boost::optional<std::string> getAttributeValue(const std::string&) const;
+
+			void addAttribute(const std::string& name, const std::string& ns, const std::string& value);
+
+			const std::vector<Entry>& getEntries() const {
+				return attributes;
 			}
+
+		private:
+			typedef std::vector<Entry> AttributeValueMap;
+			AttributeValueMap attributes;
 	};
 }
-
-#endif
diff --git a/Swiften/Parser/ExpatParser.cpp b/Swiften/Parser/ExpatParser.cpp
index 88be752..448b199 100644
--- a/Swiften/Parser/ExpatParser.cpp
+++ b/Swiften/Parser/ExpatParser.cpp
@@ -30,7 +30,7 @@ static void handleStartElement(void* client, const XML_Char* name, const XML_Cha
 			nsAttributePair.second = nsAttributePair.first;
 			nsAttributePair.first = "";
 		}
-		attributeValues[nsAttributePair.second] = std::string(*(currentAttribute+1));
+		attributeValues.addAttribute(nsAttributePair.second, nsAttributePair.first, std::string(*(currentAttribute+1)));
 		currentAttribute += 2;
 	}
 
diff --git a/Swiften/Parser/IQParser.cpp b/Swiften/Parser/IQParser.cpp
index e0883f2..62d775f 100644
--- a/Swiften/Parser/IQParser.cpp
+++ b/Swiften/Parser/IQParser.cpp
@@ -5,8 +5,9 @@
  */
 
 #include <iostream>
+#include <boost/optional.hpp>
 
-#include "Swiften/Parser/IQParser.h"
+#include <Swiften/Parser/IQParser.h>
 
 namespace Swift {
 
@@ -15,22 +16,22 @@ IQParser::IQParser(PayloadParserFactoryCollection* factories) :
 }
 
 void IQParser::handleStanzaAttributes(const AttributeMap& attributes) {
-	AttributeMap::const_iterator type = attributes.find("type");
-	if (type != attributes.end()) {
-		if (type->second == "set") {
+	boost::optional<std::string> type = attributes.getAttributeValue("type");
+	if (type) {
+		if (*type == "set") {
 			getStanzaGeneric()->setType(IQ::Set);
 		}
-		else if (type->second == "get") {
+		else if (*type == "get") {
 			getStanzaGeneric()->setType(IQ::Get);
 		}
-		else if (type->second == "result") {
+		else if (*type == "result") {
 			getStanzaGeneric()->setType(IQ::Result);
 		}
-		else if (type->second == "error") {
+		else if (*type == "error") {
 			getStanzaGeneric()->setType(IQ::Error);
 		}
 		else {
-			std::cerr << "Unknown IQ type: " << type->second << std::endl;
+			std::cerr << "Unknown IQ type: " << *type << std::endl;
 			getStanzaGeneric()->setType(IQ::Get);
 		}
 	}
diff --git a/Swiften/Parser/IQParser.h b/Swiften/Parser/IQParser.h
index e104dc4..35ab132 100644
--- a/Swiften/Parser/IQParser.h
+++ b/Swiften/Parser/IQParser.h
@@ -4,8 +4,7 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#ifndef SWIFTEN_IQParser_H
-#define SWIFTEN_IQParser_H
+#pragma once
 
 #include "Swiften/Parser/GenericStanzaParser.h"
 #include "Swiften/Elements/IQ.h"
@@ -19,5 +18,3 @@ namespace Swift {
 			virtual void handleStanzaAttributes(const AttributeMap&);
 	};
 }
-
-#endif
diff --git a/Swiften/Parser/LibXMLParser.cpp b/Swiften/Parser/LibXMLParser.cpp
index 34db4ca..0b15848 100644
--- a/Swiften/Parser/LibXMLParser.cpp
+++ b/Swiften/Parser/LibXMLParser.cpp
@@ -15,10 +15,18 @@
 
 namespace Swift {
 
-static void handleStartElement(void *client, const xmlChar* name, const xmlChar*, const xmlChar* xmlns, int, const xmlChar**, int nbAttributes, int, const xmlChar ** attributes) {
+static void handleStartElement(void *client, const xmlChar* name, const xmlChar*, const xmlChar* xmlns, int, const xmlChar**, int nbAttributes, int nbDefaulted, const xmlChar ** attributes) {
 	AttributeMap attributeValues;
+	if (nbDefaulted != 0) {
+		// Just because i don't understand what this means yet :-)
+		std::cerr << "Unexpected nbDefaulted on XML element" << std::endl;
+	}
 	for (int i = 0; i < nbAttributes*5; i += 5) {
-		attributeValues[std::string(reinterpret_cast<const char*>(attributes[i]))] = std::string(reinterpret_cast<const char*>(attributes[i+3]), attributes[i+4]-attributes[i+3]);
+		std::string attributeNS = "";
+		if (attributes[i+2]) {
+			attributeNS = std::string(reinterpret_cast<const char*>(attributes[i+2]));
+		}
+		attributeValues.addAttribute(std::string(reinterpret_cast<const char*>(attributes[i])), attributeNS, std::string(reinterpret_cast<const char*>(attributes[i+3]), attributes[i+4]-attributes[i+3]));
 	}
 	static_cast<XMLParserClient*>(client)->handleStartElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string()), attributeValues);
 }
diff --git a/Swiften/Parser/MessageParser.cpp b/Swiften/Parser/MessageParser.cpp
index 5f4d59c..7f5e6d4 100644
--- a/Swiften/Parser/MessageParser.cpp
+++ b/Swiften/Parser/MessageParser.cpp
@@ -4,9 +4,9 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#include <iostream>
+#include <boost/optional.hpp>
 
-#include "Swiften/Parser/MessageParser.h"
+#include <Swiften/Parser/MessageParser.h>
 
 namespace Swift {
 
@@ -16,18 +16,18 @@ MessageParser::MessageParser(PayloadParserFactoryCollection* factories) :
 }
 
 void MessageParser::handleStanzaAttributes(const AttributeMap& attributes) {
-	AttributeMap::const_iterator type = attributes.find("type");
-	if (type != attributes.end()) {
-		if (type->second == "chat") {
+	boost::optional<std::string> type = attributes.getAttributeValue("type");
+	if (type) {
+		if (*type == "chat") {
 			getStanzaGeneric()->setType(Message::Chat);
 		}
-		else if (type->second == "error") {
+		else if (*type == "error") {
 			getStanzaGeneric()->setType(Message::Error);
 		}
-		else if (type->second == "groupchat") {
+		else if (*type == "groupchat") {
 			getStanzaGeneric()->setType(Message::Groupchat);
 		}
-		else if (type->second == "headline") {
+		else if (*type == "headline") {
 			getStanzaGeneric()->setType(Message::Headline);
 		}
 		else {
diff --git a/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp b/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp
index e1fcb20..2e9e87a 100644
--- a/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp
+++ b/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp
@@ -15,7 +15,7 @@ DiscoInfoParser::DiscoInfoParser() : level_(TopLevel), formParser_(NULL) {
 void DiscoInfoParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
 	if (level_ == PayloadLevel) {
 		if (element == "identity") {
-			getPayloadInternal()->addIdentity(DiscoInfo::Identity(attributes.getAttribute("name"), attributes.getAttribute("category"), attributes.getAttribute("type"), attributes.getAttribute("lang")));
+			getPayloadInternal()->addIdentity(DiscoInfo::Identity(attributes.getAttribute("name"), attributes.getAttribute("category"), attributes.getAttribute("type"), attributes.getAttribute("lang", "http://www.w3.org/XML/1998/namespace")));
 		}
 		else if (element == "feature") {
 			getPayloadInternal()->addFeature(attributes.getAttribute("var"));
diff --git a/Swiften/Parser/PayloadParsers/RosterParser.cpp b/Swiften/Parser/PayloadParsers/RosterParser.cpp
index 0da9f48..5fba30b 100644
--- a/Swiften/Parser/PayloadParsers/RosterParser.cpp
+++ b/Swiften/Parser/PayloadParsers/RosterParser.cpp
@@ -5,6 +5,9 @@
  */
 
 #include "Swiften/Parser/PayloadParsers/RosterParser.h"
+
+#include <boost/optional.hpp>
+
 #include "Swiften/Parser/SerializingParser.h"
 
 namespace Swift {
@@ -14,9 +17,9 @@ RosterParser::RosterParser() : level_(TopLevel), inItem_(false), unknownContentP
 
 void RosterParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
 	if (level_ == TopLevel) {
-		AttributeMap::const_iterator i = attributes.find("ver");
-		if (i != attributes.end()) {
-			getPayloadInternal()->setVersion(i->second);
+		boost::optional<std::string> ver = attributes.getAttributeValue("ver");
+		if (ver) {
+			getPayloadInternal()->setVersion(*ver);
 		}
 	}
 	else if (level_ == PayloadLevel) {
diff --git a/Swiften/Parser/PresenceParser.cpp b/Swiften/Parser/PresenceParser.cpp
index 845ccf0..867155f 100644
--- a/Swiften/Parser/PresenceParser.cpp
+++ b/Swiften/Parser/PresenceParser.cpp
@@ -5,6 +5,7 @@
  */
 
 #include <iostream>
+#include <boost/optional.hpp>
 
 #include "Swiften/Parser/PresenceParser.h"
 
@@ -15,31 +16,31 @@ PresenceParser::PresenceParser(PayloadParserFactoryCollection* factories) :
 }
 
 void PresenceParser::handleStanzaAttributes(const AttributeMap& attributes) {
-	AttributeMap::const_iterator type = attributes.find("type");
-	if (type != attributes.end()) {
-		if (type->second == "unavailable") {
+	boost::optional<std::string> type = attributes.getAttributeValue("type");
+	if (type) {
+		if (*type == "unavailable") {
 			getStanzaGeneric()->setType(Presence::Unavailable);
 		}
-		else if (type->second == "probe") {
+		else if (*type == "probe") {
 			getStanzaGeneric()->setType(Presence::Probe);
 		}
-		else if (type->second == "subscribe") {
+		else if (*type == "subscribe") {
 			getStanzaGeneric()->setType(Presence::Subscribe);
 		}
-		else if (type->second == "subscribed") {
+		else if (*type == "subscribed") {
 			getStanzaGeneric()->setType(Presence::Subscribed);
 		}
-		else if (type->second == "unsubscribe") {
+		else if (*type == "unsubscribe") {
 			getStanzaGeneric()->setType(Presence::Unsubscribe);
 		}
-		else if (type->second == "unsubscribed") {
+		else if (*type == "unsubscribed") {
 			getStanzaGeneric()->setType(Presence::Unsubscribed);
 		}
-		else if (type->second == "error") {
+		else if (*type == "error") {
 			getStanzaGeneric()->setType(Presence::Error);
 		}
 		else {
-			std::cerr << "Unknown Presence type: " << type->second << std::endl;
+			std::cerr << "Unknown Presence type: " << *type << std::endl;
 			getStanzaGeneric()->setType(Presence::Available);
 		}
 	}
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index cbb2190..2bd5aff 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -6,6 +6,7 @@ myenv.MergeFlags(swiften_env.get("LIBXML_FLAGS", ""))
 myenv.MergeFlags(swiften_env.get("EXPAT_FLAGS", ""))
 
 sources = [
+		"AttributeMap.cpp",
 		"AuthRequestParser.cpp",
 		"AuthChallengeParser.cpp",
 		"AuthSuccessParser.cpp",
diff --git a/Swiften/Parser/SerializingParser.cpp b/Swiften/Parser/SerializingParser.cpp
index 43dfc51..03b9575 100644
--- a/Swiften/Parser/SerializingParser.cpp
+++ b/Swiften/Parser/SerializingParser.cpp
@@ -7,7 +7,6 @@
 #include "Swiften/Parser/SerializingParser.h"
 #include "Swiften/Serializer/XML/XMLTextNode.h"
 #include "Swiften/Base/foreach.h"
-#include <iostream>
 
 namespace Swift {
 
@@ -16,8 +15,9 @@ SerializingParser::SerializingParser() {
 
 void SerializingParser::handleStartElement(const std::string& tag, const std::string&  ns, const AttributeMap& attributes) {
 	boost::shared_ptr<XMLElement> element(new XMLElement(tag, ns));
-	for (AttributeMap::const_iterator i = attributes.begin(); i != attributes.end(); ++i) {
-		element->setAttribute((*i).first, (*i).second);
+	// FIXME: Ignoring attribute namespace
+	foreach (const AttributeMap::Entry& e, attributes.getEntries()) {
+		element->setAttribute(e.getAttribute().getName(), e.getValue());
 	}
 
 	if (elementStack_.empty()) {
diff --git a/Swiften/Parser/StanzaParser.cpp b/Swiften/Parser/StanzaParser.cpp
index 64c4901..051f37e 100644
--- a/Swiften/Parser/StanzaParser.cpp
+++ b/Swiften/Parser/StanzaParser.cpp
@@ -7,6 +7,7 @@
 #include "Swiften/Parser/StanzaParser.h"
 
 #include <iostream>
+#include <boost/optional.hpp>
 #include <cassert>
 
 #include "Swiften/Parser/PayloadParser.h"
@@ -39,17 +40,17 @@ void StanzaParser::handleStartElement(const std::string& element, const std::str
 		currentPayloadParser_->handleStartElement(element, ns, attributes);
 	}
 	else {
-		AttributeMap::const_iterator from = attributes.find("from");
-		if (from != attributes.end()) {
-			getStanza()->setFrom(JID(from->second));
+		boost::optional<std::string> from = attributes.getAttributeValue("from");
+		if (from) {
+			getStanza()->setFrom(JID(*from));
 		}
-		AttributeMap::const_iterator to = attributes.find("to");
-		if (to != attributes.end()) {
-			getStanza()->setTo(JID(to->second));
+		boost::optional<std::string> to = attributes.getAttributeValue("to");
+		if (to) {
+			getStanza()->setTo(JID(*to));
 		}
-		AttributeMap::const_iterator id = attributes.find("id");
-		if (id != attributes.end()) {
-			getStanza()->setID(id->second);
+		boost::optional<std::string> id = attributes.getAttributeValue("id");
+		if (id) {
+			getStanza()->setID(*id);
 		}
 		handleStanzaAttributes(attributes);
 	}
diff --git a/Swiften/Parser/UnitTest/AttributeMapTest.cpp b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
index fb68f29..8e2ccff 100644
--- a/Swiften/Parser/UnitTest/AttributeMapTest.cpp
+++ b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
@@ -14,6 +14,7 @@ using namespace Swift;
 class AttributeMapTest : public CppUnit::TestFixture
 {
 		CPPUNIT_TEST_SUITE(AttributeMapTest);
+		CPPUNIT_TEST(testGetAttribute_Namespaced);
 		CPPUNIT_TEST(testGetBoolAttribute_True);
 		CPPUNIT_TEST(testGetBoolAttribute_1);
 		CPPUNIT_TEST(testGetBoolAttribute_False);
@@ -24,39 +25,46 @@ class AttributeMapTest : public CppUnit::TestFixture
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
-		AttributeMapTest() {}
+		void testGetAttribute_Namespaced() {
+			AttributeMap testling;
+			testling.addAttribute("lang", "", "nl");
+			testling.addAttribute("lang", "http://www.w3.org/XML/1998/namespace", "en");
+			testling.addAttribute("lang", "", "fr");
+
+			CPPUNIT_ASSERT_EQUAL(std::string("en"), testling.getAttribute("lang", "http://www.w3.org/XML/1998/namespace"));
+		}
 
 		void testGetBoolAttribute_True() {
 			AttributeMap testling;
-			testling["foo"] = "true";
+			testling.addAttribute("foo", "", "true");
 
 			CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));
 		}
 
 		void testGetBoolAttribute_1() {
 			AttributeMap testling;
-			testling["foo"] = "1";
+			testling.addAttribute("foo", "", "1");
 
 			CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));
 		}
 
 		void testGetBoolAttribute_False() {
 			AttributeMap testling;
-			testling["foo"] = "false";
+			testling.addAttribute("foo", "", "false");
 
 			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
 		}
 
 		void testGetBoolAttribute_0() {
 			AttributeMap testling;
-			testling["foo"] = "0";
+			testling.addAttribute("foo", "", "0");
 
 			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
 		}
 
 		void testGetBoolAttribute_Invalid() {
 			AttributeMap testling;
-			testling["foo"] = "bla";
+			testling.addAttribute("foo", "", "bla");
 
 			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
 		}
diff --git a/Swiften/Parser/UnitTest/StanzaParserTest.cpp b/Swiften/Parser/UnitTest/StanzaParserTest.cpp
index d57f798..2657750 100644
--- a/Swiften/Parser/UnitTest/StanzaParserTest.cpp
+++ b/Swiften/Parser/UnitTest/StanzaParserTest.cpp
@@ -40,8 +40,8 @@ class StanzaParserTest : public CppUnit::TestFixture {
 			MyStanzaParser testling(factoryCollection_);
 
 			AttributeMap attributes;
-			attributes["foo"] = "fum";
-			attributes["bar"] = "baz";
+			attributes.addAttribute("foo", "", "fum");
+			attributes.addAttribute("bar", "", "baz");
 			testling.handleStartElement("mystanza", "", attributes);
 			testling.handleStartElement("mypayload1", "", attributes);
 			testling.handleStartElement("child", "", attributes);
@@ -107,9 +107,9 @@ class StanzaParserTest : public CppUnit::TestFixture {
 			MyStanzaParser testling(factoryCollection_);
 
 			AttributeMap attributes;
-			attributes["to"] = "foo@example.com/blo";
-			attributes["from"] = "bar@example.com/baz";
-			attributes["id"] = "id-123";
+			attributes.addAttribute("to", "", "foo@example.com/blo");
+			attributes.addAttribute("from", "", "bar@example.com/baz");
+			attributes.addAttribute("id", "", "id-123");
 			testling.handleStartElement("mystanza", "", attributes);
 			testling.handleEndElement("mypayload1", "");
 
diff --git a/Swiften/Parser/UnitTest/XMLParserTest.cpp b/Swiften/Parser/UnitTest/XMLParserTest.cpp
index 426b7a0..27534a1 100644
--- a/Swiften/Parser/UnitTest/XMLParserTest.cpp
+++ b/Swiften/Parser/UnitTest/XMLParserTest.cpp
@@ -31,6 +31,8 @@ class XMLParserTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST(testParse_InErrorState);
 		CPPUNIT_TEST(testParse_Incremental);
 		CPPUNIT_TEST(testParse_WhitespaceInAttribute);
+		CPPUNIT_TEST(testParse_AttributeWithoutNamespace);
+		CPPUNIT_TEST(testParse_AttributeWithNamespace);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
@@ -46,13 +48,13 @@ class XMLParserTest : public CppUnit::TestFixture {
 
 			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
 			CPPUNIT_ASSERT_EQUAL(std::string("iq"), client_.events[0].data);
-			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.size());
-			CPPUNIT_ASSERT_EQUAL(std::string("get"), client_.events[0].attributes["type"]);
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size());
+			CPPUNIT_ASSERT_EQUAL(std::string("get"), client_.events[0].attributes.getAttribute("type"));
 			CPPUNIT_ASSERT_EQUAL(std::string(), client_.events[0].ns);
 
 			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
 			CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[1].data);
-			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[1].attributes.size());
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[1].attributes.getEntries().size());
 			CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].ns);
 
 			CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
@@ -76,7 +78,7 @@ class XMLParserTest : public CppUnit::TestFixture {
 
 			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
 			CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[0].data);
-			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].attributes.size());
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].attributes.getEntries().size());
 			CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[0].ns);
 
 			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
@@ -205,6 +207,28 @@ class XMLParserTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
 			CPPUNIT_ASSERT_EQUAL(std::string("presence"), client_.events[2].data);
 		}
+
+		void testParse_AttributeWithoutNamespace() {
+			ParserType testling(&client_);
+
+			CPPUNIT_ASSERT(testling.parse(
+				"<query xmlns='http://swift.im' attr='3'/>"));
+
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size());
+			CPPUNIT_ASSERT_EQUAL(std::string("attr"), client_.events[0].attributes.getEntries()[0].getAttribute().getName());
+			CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace());
+		}
+
+		void testParse_AttributeWithNamespace() {
+			ParserType testling(&client_);
+
+			CPPUNIT_ASSERT(testling.parse(
+				"<query xmlns='http://swift.im' xmlns:f='http://swift.im/f' f:attr='3'/>"));
+
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size());
+			CPPUNIT_ASSERT_EQUAL(std::string("attr"), client_.events[0].attributes.getEntries()[0].getAttribute().getName());
+			CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im/f"), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace());
+		}
 	
 	private:
 		class Client : public XMLParserClient {
-- 
cgit v0.10.2-6-g49f6