summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoanna Hulboj <joanna.hulboj@isode.com>2019-09-19 19:48:44 (GMT)
committerJoanna Hulboj <joanna.hulboj@isode.com>2019-09-24 10:08:15 (GMT)
commit23e2766dab6d4a3f6158eca7649cd36b644634d3 (patch)
tree1faa4b741b03704e7a70918196310a4453e699a7
parente58cf7d5d7d3bab330bccf6a098dd476fbf4dc86 (diff)
downloadswift-23e2766dab6d4a3f6158eca7649cd36b644634d3.zip
swift-23e2766dab6d4a3f6158eca7649cd36b644634d3.tar.bz2
Process attribute and element prefixes
XML (Expat/LibXML) parsing modified to process prefix information. Prefixes for attributes stored within attributes. Prefixes for elements passed in additional callback (only if prefix present). Test-information: Unit tests pass on Windows 10 and Ubuntu 18.04.1 LTS. Change-Id: Ib6b5087feed758c31895f426df6a3c7ea975f248
-rw-r--r--Swiften/Parser/Attribute.h8
-rw-r--r--Swiften/Parser/AttributeMap.cpp4
-rw-r--r--Swiften/Parser/AttributeMap.h1
-rw-r--r--Swiften/Parser/ExpatParser.cpp57
-rw-r--r--Swiften/Parser/LibXMLParser.cpp33
-rw-r--r--Swiften/Parser/UnitTest/AttributeMapTest.cpp17
-rw-r--r--Swiften/Parser/UnitTest/XMLParserTest.cpp59
-rw-r--r--Swiften/Parser/XMLParserClient.cpp6
-rw-r--r--Swiften/Parser/XMLParserClient.h8
9 files changed, 160 insertions, 33 deletions
diff --git a/Swiften/Parser/Attribute.h b/Swiften/Parser/Attribute.h
index f54317e..07e63b4 100644
--- a/Swiften/Parser/Attribute.h
+++ b/Swiften/Parser/Attribute.h
@@ -16,2 +16,5 @@ namespace Swift {
+ Attribute(const std::string& name, const std::string& ns, const std::string& prefix) : name(name), ns(ns), prefix(prefix) {
+ }
+
const std::string& getName() const {
@@ -24,2 +27,6 @@ namespace Swift {
+ const std::string& getPrefix() const {
+ return prefix;
+ }
+
bool operator==(const Attribute& o) const {
@@ -31,2 +38,3 @@ namespace Swift {
std::string ns;
+ std::string prefix;
};
diff --git a/Swiften/Parser/AttributeMap.cpp b/Swiften/Parser/AttributeMap.cpp
index f6767de..7814a64 100644
--- a/Swiften/Parser/AttributeMap.cpp
+++ b/Swiften/Parser/AttributeMap.cpp
@@ -56 +56,5 @@ void AttributeMap::addAttribute(const std::string& name, const std::string& ns,
}
+
+void AttributeMap::addAttribute(const std::string& name, const std::string& ns, const std::string& prefix, const std::string& value) {
+ attributes.push_back(Entry(Attribute(name, ns, prefix), value));
+}
diff --git a/Swiften/Parser/AttributeMap.h b/Swiften/Parser/AttributeMap.h
index 804d6aa..26d5826 100644
--- a/Swiften/Parser/AttributeMap.h
+++ b/Swiften/Parser/AttributeMap.h
@@ -45,2 +45,3 @@ namespace Swift {
void addAttribute(const std::string& name, const std::string& ns, const std::string& value);
+ void addAttribute(const std::string& name, const std::string& ns, const std::string& prefix, const std::string& value);
diff --git a/Swiften/Parser/ExpatParser.cpp b/Swiften/Parser/ExpatParser.cpp
index 640d561..6c3845a 100644
--- a/Swiften/Parser/ExpatParser.cpp
+++ b/Swiften/Parser/ExpatParser.cpp
@@ -13,2 +13,4 @@
+#include <boost/algorithm/string.hpp>
+
#include <expat.h>
@@ -20,2 +22,29 @@
+namespace {
+struct XmlInfo {
+ std::string prefix;
+ std::string uri;
+ std::string name;
+};
+
+XmlInfo splitExpatInfo(const std::string& s, char sep) {
+ // name
+ // uri|name
+ // uri|name|prefix
+ std::vector<std::string> v;
+ boost::split(v, s, [sep](char c) {return c == sep; });
+ switch (v.size()) {
+ case 1:
+ return{ "", "", std::move(v[0]) };
+ case 2:
+ return{ "", std::move(v[0]), std::move(v[1]) };
+ case 3:
+ return{ std::move(v[2]), std::move(v[0]), std::move(v[1]) };
+ default:
+ return{ "", "", "" };
+ }
+}
+}
+
+
namespace Swift {
@@ -29,7 +58,4 @@ struct ExpatParser::Private {
static void handleStartElement(void* parser, const XML_Char* name, const XML_Char** attributes) {
- std::pair<std::string,std::string> nsTagPair = String::getSplittedAtFirst(name, NAMESPACE_SEPARATOR);
- if (nsTagPair.second == "") {
- nsTagPair.second = nsTagPair.first;
- nsTagPair.first = "";
- }
+ auto elemInfo = splitExpatInfo(name, NAMESPACE_SEPARATOR);
+
AttributeMap attributeValues;
@@ -37,8 +63,4 @@ static void handleStartElement(void* parser, const XML_Char* name, const XML_Cha
while (*currentAttribute) {
- std::pair<std::string,std::string> nsAttributePair = String::getSplittedAtFirst(*currentAttribute, NAMESPACE_SEPARATOR);
- if (nsAttributePair.second == "") {
- nsAttributePair.second = nsAttributePair.first;
- nsAttributePair.first = "";
- }
- attributeValues.addAttribute(nsAttributePair.second, nsAttributePair.first, std::string(*(currentAttribute+1)));
+ auto attribInfo = splitExpatInfo(*currentAttribute, NAMESPACE_SEPARATOR);
+ attributeValues.addAttribute(attribInfo.name, attribInfo.uri, attribInfo.prefix, std::string(*(currentAttribute+1)));
currentAttribute += 2;
@@ -46,3 +68,5 @@ static void handleStartElement(void* parser, const XML_Char* name, const XML_Cha
- static_cast<XMLParser*>(parser)->getClient()->handleStartElement(nsTagPair.second, nsTagPair.first, attributeValues);
+ auto* client = static_cast<XMLParser*>(parser)->getClient();
+ client->handleStartElementPrefix(elemInfo.prefix, elemInfo.uri, elemInfo.name, elemInfo.name, elemInfo.uri, attributeValues);
+ client->handleStartElement(elemInfo.name, elemInfo.uri, attributeValues);
}
@@ -50,8 +74,4 @@ static void handleStartElement(void* parser, const XML_Char* name, const XML_Cha
static void handleEndElement(void* parser, const XML_Char* name) {
- std::pair<std::string,std::string> nsTagPair = String::getSplittedAtFirst(name, NAMESPACE_SEPARATOR);
- if (nsTagPair.second == "") {
- nsTagPair.second = nsTagPair.first;
- nsTagPair.first = "";
- }
- static_cast<XMLParser*>(parser)->getClient()->handleEndElement(nsTagPair.second, nsTagPair.first);
+ auto elemInfo = splitExpatInfo(name, NAMESPACE_SEPARATOR);
+ static_cast<XMLParser*>(parser)->getClient()->handleEndElement(elemInfo.name, elemInfo.uri);
}
@@ -90,2 +110,3 @@ ExpatParser::ExpatParser(XMLParserClient* client, bool allowComments) : XMLParse
p->parser_ = XML_ParserCreateNS("UTF-8", NAMESPACE_SEPARATOR);
+ XML_SetReturnNSTriplet(p->parser_, true);
XML_SetUserData(p->parser_, this);
diff --git a/Swiften/Parser/LibXMLParser.cpp b/Swiften/Parser/LibXMLParser.cpp
index 4e02059..71515a7 100644
--- a/Swiften/Parser/LibXMLParser.cpp
+++ b/Swiften/Parser/LibXMLParser.cpp
@@ -19,2 +19,8 @@
+namespace {
+std::string asString(const unsigned char* s) {
+ return s ? std::string(reinterpret_cast<const char*>(s)) : std::string();
+}
+}
+
namespace Swift {
@@ -26,3 +32,3 @@ struct LibXMLParser::Private {
-static void handleStartElement(void* parser, const xmlChar* name, const xmlChar*, const xmlChar* xmlns, int nbNamespaces, const xmlChar** namespaces, int nbAttributes, int nbDefaulted, const xmlChar ** attributes) {
+static void handleStartElement(void* parser, const xmlChar* name, const xmlChar* prefix, const xmlChar* xmlns, int nbNamespaces, const xmlChar** namespaces, int nbAttributes, int nbDefaulted, const xmlChar ** attributes) {
AttributeMap attributeValues;
@@ -33,10 +39,10 @@ static void handleStartElement(void* parser, const xmlChar* name, const xmlChar*
for (int i = 0; i < nbAttributes*5; i += 5) {
- std::string attributeNS = "";
- if (attributes[i+2]) {
- attributeNS = std::string(reinterpret_cast<const char*>(attributes[i+2]));
- }
+ std::string attributeName = asString(attributes[i]);
+ std::string attributePrefix = asString(attributes[i+1]);
+ std::string attributeNS = asString(attributes[i+2]);
assert(attributes[i+4] >= attributes[i+3]);
attributeValues.addAttribute(
- std::string(reinterpret_cast<const char*>(attributes[i])),
+ attributeName,
attributeNS,
+ attributePrefix,
std::string(reinterpret_cast<const char*>(attributes[i+3]),
@@ -44,8 +50,13 @@ static void handleStartElement(void* parser, const xmlChar* name, const xmlChar*
}
+ auto* client = static_cast<XMLParser*>(parser)->getClient();
for (auto i = 0; i < nbNamespaces * 2; i += 2) {
- const auto prefix = namespaces[i] ? std::string(reinterpret_cast<const char*>(namespaces[i])) : "";
- const auto uri = std::string(reinterpret_cast<const char*>(namespaces[i + 1]));
- static_cast<XMLParser*>(parser)->getClient()->handleNamespaceDeclaration(prefix, uri);
+ const auto prefix = asString(namespaces[i]);
+ const auto uri = asString(namespaces[i + 1]);
+ client->handleNamespaceDeclaration(prefix, uri);
}
- static_cast<XMLParser*>(parser)->getClient()->handleStartElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string()), attributeValues);
+ auto nameStr = asString(name);
+ auto xmlsnsStr = asString(xmlns);
+ auto prefixStr = asString(prefix);
+ client->handleStartElementPrefix(prefixStr, xmlsnsStr, nameStr, nameStr, xmlsnsStr, attributeValues);
+ client->handleStartElement(nameStr, xmlsnsStr, attributeValues);
}
@@ -53,3 +64,3 @@ static void handleStartElement(void* parser, const xmlChar* name, const xmlChar*
static void handleEndElement(void *parser, const xmlChar* name, const xmlChar*, const xmlChar* xmlns) {
- static_cast<XMLParser*>(parser)->getClient()->handleEndElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string()));
+ static_cast<XMLParser*>(parser)->getClient()->handleEndElement(asString(name), asString(xmlns));
}
diff --git a/Swiften/Parser/UnitTest/AttributeMapTest.cpp b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
index 4529eac..d9335c1 100644
--- a/Swiften/Parser/UnitTest/AttributeMapTest.cpp
+++ b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
@@ -17,2 +17,3 @@ class AttributeMapTest : public CppUnit::TestFixture
CPPUNIT_TEST(testGetAttribute_Namespaced);
+ CPPUNIT_TEST(testGetAttribute_Namespaced_Prefix);
CPPUNIT_TEST(testGetBoolAttribute_True);
@@ -36,2 +37,18 @@ class AttributeMapTest : public CppUnit::TestFixture
+ void testGetAttribute_Namespaced_Prefix() {
+ AttributeMap testling;
+ testling.addAttribute("lang", "", "prefix", "nl");
+ testling.addAttribute("lang", "http://www.w3.org/XML/1998/namespace", "prefix", "en");
+ testling.addAttribute("lang", "", "prefix", "fr");
+
+ CPPUNIT_ASSERT_EQUAL(std::string("en"), testling.getAttribute("lang", "http://www.w3.org/XML/1998/namespace"));
+ const auto& entries = testling.getEntries();
+ auto it = std::find_if(entries.begin(), entries.end(), [](const AttributeMap::Entry& e) {
+ return e.getValue() == "en";
+ });
+ const bool found = it != entries.end();
+ CPPUNIT_ASSERT_EQUAL(true, found);
+ CPPUNIT_ASSERT_EQUAL(std::string("prefix"), it->getAttribute().getPrefix());
+ }
+
void testGetBoolAttribute_True() {
diff --git a/Swiften/Parser/UnitTest/XMLParserTest.cpp b/Swiften/Parser/UnitTest/XMLParserTest.cpp
index 63d30ea..4db694e 100644
--- a/Swiften/Parser/UnitTest/XMLParserTest.cpp
+++ b/Swiften/Parser/UnitTest/XMLParserTest.cpp
@@ -37,2 +37,3 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testParse_AttributeWithNamespace);
+ CPPUNIT_TEST(testParse_AttributeWithNamespaceNoPrefix);
CPPUNIT_TEST(testParse_BillionLaughs);
@@ -45,2 +46,3 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testParse_ProcessingInstructions);
+ CPPUNIT_TEST(testParse_ProcessingPrefixedElement);
CPPUNIT_TEST_SUITE_END();
@@ -266,2 +268,3 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace());
+ CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getPrefix());
}
@@ -277,2 +280,18 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im/f"), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace());
+ CPPUNIT_ASSERT_EQUAL(std::string("f"), client_.events[0].attributes.getEntries()[0].getAttribute().getPrefix());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events[0].namespaces.size());
+ CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im"), client_.events[0].namespaces[""]);
+ CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im/f"), client_.events[0].namespaces["f"]);
+ }
+
+ void testParse_AttributeWithNamespaceNoPrefix() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse(
+ "<query xmlns='http://swift.im' xmlns:f='http://swift.im/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(""), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace());
+ CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getPrefix());
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events[0].namespaces.size());
@@ -375,2 +394,20 @@ class XMLParserTest : public CppUnit::TestFixture {
+ void testParse_ProcessingPrefixedElement() {
+ client_.testingStartElementPrefix = true;
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse("<prefix:message xmlns='uri' xmlns:prefix='uriPrefix'/>"));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events.size());
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElementPrefix, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), client_.events[0].data);
+ CPPUNIT_ASSERT_EQUAL(std::string("uriPrefix"), client_.events[0].ns);
+ CPPUNIT_ASSERT_EQUAL(std::string("prefix"), client_.events[0].prefix);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), client_.events[1].data);
+ CPPUNIT_ASSERT_EQUAL(std::string("uriPrefix"), client_.events[1].ns);
+ }
+
private:
@@ -379,3 +416,3 @@ class XMLParserTest : public CppUnit::TestFixture {
using NamespaceMap = std::unordered_map<std::string /* prefix */, std::string /* uri */>;
- enum Type { StartElement, EndElement, CharacterData, NamespaceDefined };
+ enum Type { StartElement, StartElementPrefix, EndElement, CharacterData, NamespaceDefined };
struct Event {
@@ -385,7 +422,15 @@ class XMLParserTest : public CppUnit::TestFixture {
const std::string& ns,
+ const std::string& prefix,
+ const AttributeMap& attributes,
+ NamespaceMap namespaces)
+ : type(type), data(data), ns(ns), prefix(prefix), attributes(attributes), namespaces(std::move(namespaces)) {}
+ Event(
+ Type type,
+ const std::string& data,
+ const std::string& ns,
const AttributeMap& attributes,
NamespaceMap namespaces = {})
- : type(type), data(data), ns(ns), attributes(attributes), namespaces(std::move(namespaces)) {}
+ : Event(type, data, ns, {}, attributes, std::move(namespaces)) {}
Event(Type type, const std::string& data, const std::string& ns = std::string())
- : type(type), data(data), ns(ns) {}
+ : Event(type, data, ns, "", AttributeMap(), NamespaceMap()) {}
@@ -394,2 +439,3 @@ class XMLParserTest : public CppUnit::TestFixture {
std::string ns;
+ std::string prefix;
AttributeMap attributes;
@@ -401,2 +447,3 @@ class XMLParserTest : public CppUnit::TestFixture {
void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) override {
+ if (testingStartElementPrefix) return;
events.push_back(Event(StartElement, element, ns, attributes, std::move(namespaces_)));
@@ -404,2 +451,7 @@ class XMLParserTest : public CppUnit::TestFixture {
+ void handleStartElementPrefix(const std::string& prefix, const std::string& uri, const std::string& name, const std::string&, const std::string&, const AttributeMap&) override {
+ if (!testingStartElementPrefix) return;
+ events.push_back(Event(StartElementPrefix, name, uri, prefix, AttributeMap(), NamespaceMap()));
+ }
+
void handleEndElement(const std::string& element, const std::string& ns) override {
@@ -417,2 +469,3 @@ class XMLParserTest : public CppUnit::TestFixture {
std::vector<Event> events;
+ bool testingStartElementPrefix = false;
private:
diff --git a/Swiften/Parser/XMLParserClient.cpp b/Swiften/Parser/XMLParserClient.cpp
index 40be4e8..847e1f3 100644
--- a/Swiften/Parser/XMLParserClient.cpp
+++ b/Swiften/Parser/XMLParserClient.cpp
@@ -13,2 +13,8 @@ XMLParserClient::~XMLParserClient() {
+void XMLParserClient::handleStartElement(const std::string&, const std::string&, const AttributeMap&) {
+}
+
+void XMLParserClient::handleStartElementPrefix(const std::string&, const std::string&, const std::string&, const std::string&, const std::string&, const AttributeMap&) {
+}
+
void XMLParserClient::handleNamespaceDeclaration(const std::string&, const std::string&) {
diff --git a/Swiften/Parser/XMLParserClient.h b/Swiften/Parser/XMLParserClient.h
index 0682320..f519646 100644
--- a/Swiften/Parser/XMLParserClient.h
+++ b/Swiften/Parser/XMLParserClient.h
@@ -16,3 +16,9 @@ namespace Swift {
- virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) = 0;
+ /**
+ * Client will have to implement only one of the following methods depending on whether
+ * he is interested in processing the element prefix or not.
+ */
+ virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes);
+ virtual void handleStartElementPrefix(const std::string& prefix, const std::string& uri, const std::string& name, const std::string& element, const std::string& ns, const AttributeMap& attributes);
+
virtual void handleEndElement(const std::string& element, const std::string& ns) = 0;