Skip to content

Commit

Permalink
Signal namespace declarations to ParserClients
Browse files Browse the repository at this point in the history
Prior to calling handleStartElement, the ParserClient
handleNamespaceDeclaration will fire for each namespace declared on the
element.

Test-Information:

Unit tests pass on Debian 9 for both expat and libxml2

Change-Id: Ic42e83aee83edfbb2aa5c971997808eb6e133223
  • Loading branch information
intosi committed Jul 22, 2019
1 parent f4b6bfb commit f6fb85b
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 10 deletions.
5 changes: 5 additions & 0 deletions Swiften/Parser/ExpatParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ static void handleCharacterData(void* parser, const XML_Char* data, int len) {
static void handleXMLDeclaration(void*, const XML_Char*, const XML_Char*, int) {
}

static void handleNamespaceDeclaration(void* parser, const XML_Char* prefix, const XML_Char* uri) {
static_cast<XMLParser*>(parser)->getClient()->handleNamespaceDeclaration(std::string(prefix ? prefix : ""), std::string(uri ? uri : ""));
}

static void handleEntityDeclaration(void* parser, const XML_Char*, int, const XML_Char*, int, const XML_Char*, const XML_Char*, const XML_Char*, const XML_Char*) {
static_cast<ExpatParser*>(parser)->stopParser();
}
Expand All @@ -76,6 +80,7 @@ ExpatParser::ExpatParser(XMLParserClient* client) : XMLParser(client), p(new Pri
XML_SetCharacterDataHandler(p->parser_, handleCharacterData);
XML_SetXmlDeclHandler(p->parser_, handleXMLDeclaration);
XML_SetEntityDeclHandler(p->parser_, handleEntityDeclaration);
XML_SetNamespaceDeclHandler(p->parser_, handleNamespaceDeclaration, nullptr);
}

ExpatParser::~ExpatParser() {
Expand Down
7 changes: 6 additions & 1 deletion Swiften/Parser/LibXMLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct LibXMLParser::Private {
xmlParserCtxtPtr context_;
};

static void handleStartElement(void* parser, const xmlChar* name, const xmlChar*, const xmlChar* xmlns, int, const xmlChar**, int nbAttributes, int nbDefaulted, const xmlChar ** attributes) {
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) {
AttributeMap attributeValues;
if (nbDefaulted != 0) {
// Just because i don't understand what this means yet :-)
Expand All @@ -42,6 +42,11 @@ static void handleStartElement(void* parser, const xmlChar* name, const xmlChar*
std::string(reinterpret_cast<const char*>(attributes[i+3]),
static_cast<size_t>(attributes[i+4]-attributes[i+3])));
}
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);
}
static_cast<XMLParser*>(parser)->getClient()->handleStartElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string()), attributeValues);
}

Expand Down
35 changes: 28 additions & 7 deletions Swiften/Parser/UnitTest/XMLParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <unordered_map>
#include <vector>

#include <string>
Expand Down Expand Up @@ -61,6 +62,9 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[1].data);
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(static_cast<size_t>(1), client_.events[1].namespaces.size());
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[1].namespaces.count(""));
CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].namespaces[""]);

CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[2].data);
Expand All @@ -85,10 +89,13 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[0].data);
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(static_cast<size_t>(1), client_.events[0].namespaces.size());
CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[0].namespaces[""]);

CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
CPPUNIT_ASSERT_EQUAL(std::string("name"), client_.events[1].data);
CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].ns);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[1].namespaces.size());

CPPUNIT_ASSERT_EQUAL(Client::CharacterData, client_.events[2].type);
CPPUNIT_ASSERT_EQUAL(std::string("Swift"), client_.events[2].data);
Expand Down Expand Up @@ -161,6 +168,8 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
CPPUNIT_ASSERT_EQUAL(std::string("x"), client_.events[0].data);
CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[0].ns);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].namespaces.size());
CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[0].namespaces["p"]);

CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
CPPUNIT_ASSERT_EQUAL(std::string("y"), client_.events[1].data);
Expand Down Expand Up @@ -262,6 +271,9 @@ class XMLParserTest : public CppUnit::TestFixture {
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());
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_BillionLaughs() {
Expand Down Expand Up @@ -301,6 +313,7 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
CPPUNIT_ASSERT_EQUAL(std::string("foo:bar"), client_.events[0].data);
CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].ns);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].namespaces.size());

CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[1].data);
Expand Down Expand Up @@ -328,38 +341,46 @@ class XMLParserTest : public CppUnit::TestFixture {
private:
class Client : public XMLParserClient {
public:
enum Type { StartElement, EndElement, CharacterData };
using NamespaceMap = std::unordered_map<std::string /* prefix */, std::string /* uri */>;
enum Type { StartElement, EndElement, CharacterData, NamespaceDefined };
struct Event {
Event(
Type type,
const std::string& data,
const std::string& ns,
const AttributeMap& attributes)
: type(type), data(data), ns(ns), attributes(attributes) {}
const AttributeMap& attributes,
NamespaceMap namespaces = {})
: type(type), data(data), ns(ns), attributes(attributes), namespaces(std::move(namespaces)) {}
Event(Type type, const std::string& data, const std::string& ns = std::string())
: type(type), data(data), ns(ns) {}

Type type;
std::string data;
std::string ns;
AttributeMap attributes;
NamespaceMap namespaces;
};

Client() {}

virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
events.push_back(Event(StartElement, element, ns, attributes));
void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) override {
events.push_back(Event(StartElement, element, ns, attributes, std::move(namespaces_)));
}

virtual void handleEndElement(const std::string& element, const std::string& ns) {
void handleEndElement(const std::string& element, const std::string& ns) override {
events.push_back(Event(EndElement, element, ns));
}

virtual void handleCharacterData(const std::string& data) {
void handleCharacterData(const std::string& data) override {
events.push_back(Event(CharacterData, data));
}

void handleNamespaceDeclaration(const std::string& prefix, const std::string& uri) override {
namespaces_[prefix] = uri;
}
std::vector<Event> events;
private:
NamespaceMap namespaces_;
} client_;
};

Expand Down
5 changes: 4 additions & 1 deletion Swiften/Parser/XMLParserClient.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 Isode Limited.
* Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
Expand All @@ -11,5 +11,8 @@ namespace Swift {
XMLParserClient::~XMLParserClient() {
}

void XMLParserClient::handleNamespaceDeclaration(const std::string&, const std::string&) {
}

}

10 changes: 9 additions & 1 deletion Swiften/Parser/XMLParserClient.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 Isode Limited.
* Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
Expand All @@ -17,5 +17,13 @@ namespace Swift {
virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) = 0;
virtual void handleEndElement(const std::string& element, const std::string& ns) = 0;
virtual void handleCharacterData(const std::string& data) = 0;

/**
* Signal that a namespace prefix has been declared
* This callback might be called multiple times for a single element,
* and will trigger before the corresponding \ref handleStartElement
* is called.
*/
virtual void handleNamespaceDeclaration(const std::string& prefix, const std::string& uri);
};
}

0 comments on commit f6fb85b

Please sign in to comment.