diff options
author | Tim Robbings <tim.robbings@isode.com> | 2015-01-26 17:33:20 (GMT) |
---|---|---|
committer | Kevin Smith <kevin.smith@isode.com> | 2015-02-17 12:17:35 (GMT) |
commit | 55461d1b5f97591b4ab9510896ca1bc5b5e2a71f (patch) | |
tree | 17c79419d06c6740bb6ff04f6e17a13e7c4533a5 /Swiften | |
parent | 265b779d6766130afa8f2f166733dc172ba22dca (diff) | |
download | swift-55461d1b5f97591b4ab9510896ca1bc5b5e2a71f.zip swift-55461d1b5f97591b4ab9510896ca1bc5b5e2a71f.tar.bz2 |
Swiften XEP-0141 support
Classes to support XEP-0141 data forms layout. This includes <page/>,
<section/>, <reportedref/> and <text/> elements. The form
parser and serializer classes have also been updated to handle these new
elements, with added CPPUnit tests.
Test-information:
Tested using updated CPPUnit tests to check the parsing and serializing of
new elements. All tests complete successfully.
Change-Id: Ibeab22d2834512d06c7f656acd1ef24eea39d650
Diffstat (limited to 'Swiften')
-rw-r--r-- | Swiften/Elements/Form.h | 89 | ||||
-rw-r--r-- | Swiften/Elements/FormPage.cpp | 68 | ||||
-rw-r--r-- | Swiften/Elements/FormPage.h | 47 | ||||
-rw-r--r-- | Swiften/Elements/FormReportedRef.h | 17 | ||||
-rw-r--r-- | Swiften/Elements/FormSection.cpp | 64 | ||||
-rw-r--r-- | Swiften/Elements/FormSection.h | 44 | ||||
-rw-r--r-- | Swiften/Elements/FormText.cpp | 24 | ||||
-rw-r--r-- | Swiften/Elements/FormText.h | 26 | ||||
-rw-r--r-- | Swiften/Parser/PayloadParsers/FormParser.cpp | 94 | ||||
-rw-r--r-- | Swiften/Parser/PayloadParsers/FormParser.h | 12 | ||||
-rw-r--r-- | Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp | 46 | ||||
-rw-r--r-- | Swiften/SConscript | 3 | ||||
-rw-r--r-- | Swiften/Serializer/PayloadSerializers/FormSerializer.cpp | 76 | ||||
-rw-r--r-- | Swiften/Serializer/PayloadSerializers/FormSerializer.h | 13 | ||||
-rw-r--r-- | Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp | 69 |
15 files changed, 654 insertions, 38 deletions
diff --git a/Swiften/Elements/Form.h b/Swiften/Elements/Form.h index 42fe7ed..c1d7a5b 100644 --- a/Swiften/Elements/Form.h +++ b/Swiften/Elements/Form.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -11,6 +11,8 @@ #include <Swiften/Base/API.h> #include <Swiften/Elements/Payload.h> +#include <Swiften/Elements/FormPage.h> +#include <Swiften/Elements/FormSection.h> #include <Swiften/Elements/FormField.h> #include <Swiften/JID/JID.h> @@ -30,39 +32,90 @@ namespace Swift { public: Form(Type type = FormType) : type_(type) {} - /** Add a field to the form. - * @param field New field - must not be null. - */ - void addField(boost::shared_ptr<FormField> field) {assert(field); fields_.push_back(field); } - const std::vector<boost::shared_ptr<FormField> >& getFields() const { return fields_; } - void clearFields() { fields_.clear(); } - void setTitle(const std::string& title) { title_ = title; } - const std::string& getTitle() const { return title_; } + void addPage(boost::shared_ptr<FormPage> page) { + assert(page); + pages_.push_back(page); + } - void setInstructions(const std::string& instructions) { instructions_ = instructions; } - const std::string& getInstructions() const { return instructions_; } + const std::vector<boost::shared_ptr<FormPage> >& getPages() const { + return pages_; + } - Type getType() const { return type_; } - void setType(Type type) { type_ = type; } + void addField(boost::shared_ptr<FormField> field) { + assert(field); + fields_.push_back(field); + } - std::string getFormType() const; + const std::vector<boost::shared_ptr<FormField> >& getFields() const { + return fields_; + } - FormField::ref getField(const std::string& name) const; + void clearFields() { + fields_.clear(); + } - void addReportedField(FormField::ref field); - const std::vector<FormField::ref>& getReportedFields() const; - void clearReportedFields() { reportedFields_.clear(); } + void addTextElement(boost::shared_ptr<FormText> text) { + assert(text); + textElements_.push_back(text); + } + + const std::vector<boost::shared_ptr<FormText> >& getTextElements() const { + return textElements_; + } + + void addReportedRef(boost::shared_ptr<FormReportedRef> reportedRef) { + assert(reportedRef); + reportedRefs_.push_back(reportedRef); + } + + const std::vector<boost::shared_ptr<FormReportedRef> >& getReportedRefs() const { + return reportedRefs_; + } + + void setTitle(const std::string& title) { + title_ = title; + } + + const std::string& getTitle() const { + return title_; + } + void setInstructions(const std::string& instructions) { + instructions_ = instructions; + } + + const std::string& getInstructions() const { + return instructions_; + } + + Type getType() const { + return type_; + } + + void setType(Type type) { + type_ = type; + } + + std::string getFormType() const; + FormField::ref getField(const std::string& name) const; void addItem(const FormItem& item); const std::vector<FormItem>& getItems() const; void clearItems() { items_.clear(); } void clearEmptyTextFields(); + void addReportedField(FormField::ref field); + const std::vector<FormField::ref> & getReportedFields() const; + void clearReportedFields() { reportedFields_.clear(); } + private: + std::vector<boost::shared_ptr<FormReportedRef> >reportedRefs_; + std::vector<boost::shared_ptr<FormText> > textElements_; + std::vector<boost::shared_ptr<FormPage> > pages_; std::vector<boost::shared_ptr<FormField> > fields_; std::vector<boost::shared_ptr<FormField> > reportedFields_; std::vector<FormItem> items_; + boost::shared_ptr<FormReportedRef> reportedRef_; std::string title_; std::string instructions_; Type type_; diff --git a/Swiften/Elements/FormPage.cpp b/Swiften/Elements/FormPage.cpp new file mode 100644 index 0000000..37ff32c --- /dev/null +++ b/Swiften/Elements/FormPage.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +#include <Swiften/Elements/FormPage.h> + +namespace Swift { + +FormPage::FormPage() : xmlns_("http://jabber.org/protocol/xdata-layout") { +} + +FormPage::~FormPage() { +} + +void FormPage::setLabel(const std::string& label) { + label_ = label; +} + +const std::string& FormPage::getLabel() const { + return label_; +} + +const std::string& FormPage::getXMLNS() const { + return xmlns_; +} + +void FormPage::addChildSection(boost::shared_ptr<FormSection>& section) { + childSections_.push_back(section); +} + +const std::vector<boost::shared_ptr<FormSection> >& FormPage::getChildSections() const { + return childSections_; +} + +void FormPage::addTextElement(boost::shared_ptr<FormText>& textElement) { + textElements_.push_back(textElement); +} + +const std::vector<boost::shared_ptr<FormText> >& FormPage::getTextElements() const { + return textElements_; +} + +void FormPage::addReportedRef(boost::shared_ptr<FormReportedRef>& reportedRef) { + reportedRefs_.push_back(reportedRef); +} + +const std::vector<boost::shared_ptr<FormReportedRef> >& FormPage::getReportedRefs() const { + return reportedRefs_; +} + +void FormPage::addField(boost::shared_ptr<FormField>& field) { + fields_.push_back(field); +} + +const std::vector<boost::shared_ptr<FormField> >& FormPage::getFields() const { + return fields_; +} + +void FormPage::addFieldRef(std::string ref) { + fieldRefs_.push_back(ref); +} + +const std::vector<std::string> FormPage::getFieldRefs() const { + return fieldRefs_; +} + +}
\ No newline at end of file diff --git a/Swiften/Elements/FormPage.h b/Swiften/Elements/FormPage.h new file mode 100644 index 0000000..e5ecda2 --- /dev/null +++ b/Swiften/Elements/FormPage.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +#pragma once + +#include <string> +#include <vector> +#include <boost/shared_ptr.hpp> +#include <Swiften/Base/API.h> +#include <Swiften/Elements/FormField.h> +#include <Swiften/Elements/FormReportedRef.h> +#include <Swiften/Elements/FormSection.h> +#include <Swiften/Elements/FormText.h> + +namespace Swift { + + class SWIFTEN_API FormPage { + public: + typedef boost::shared_ptr<FormPage> page; + FormPage (); + ~FormPage(); + void setLabel(const std::string& label); + const std::string& getLabel() const; + const std::string& getXMLNS() const; + void addChildSection(boost::shared_ptr<FormSection>& section); + const std::vector<boost::shared_ptr<FormSection> >& getChildSections() const; + void addTextElement(boost::shared_ptr<FormText>& textElement); + const std::vector<boost::shared_ptr<FormText> >& getTextElements() const; + void addReportedRef(boost::shared_ptr<FormReportedRef>& reportedRef); + const std::vector<boost::shared_ptr<FormReportedRef> >& getReportedRefs() const; + void addField(boost::shared_ptr<FormField>& field); + const std::vector<boost::shared_ptr<FormField> >& getFields() const; + void addFieldRef(std::string ref); + const std::vector<std::string> getFieldRefs() const; + + private: + std::string xmlns_; + std::string label_; + std::vector<boost::shared_ptr<FormText> > textElements_; + std::vector<boost::shared_ptr<FormSection> > childSections_; + std::vector<boost::shared_ptr<FormReportedRef> > reportedRefs_; + std::vector<boost::shared_ptr<FormField> > fields_; + std::vector<std::string> fieldRefs_; + }; +} diff --git a/Swiften/Elements/FormReportedRef.h b/Swiften/Elements/FormReportedRef.h new file mode 100644 index 0000000..b985167 --- /dev/null +++ b/Swiften/Elements/FormReportedRef.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +#pragma once + +#include <Swiften/Base/API.h> + +namespace Swift { + + class SWIFTEN_API FormReportedRef { + + public: + typedef boost::shared_ptr<FormReportedRef> ref; + }; +} diff --git a/Swiften/Elements/FormSection.cpp b/Swiften/Elements/FormSection.cpp new file mode 100644 index 0000000..1784714 --- /dev/null +++ b/Swiften/Elements/FormSection.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +#include <Swiften/Elements/FormSection.h> + +namespace Swift { + +FormSection::FormSection() { +} + +FormSection::~FormSection() { +} + +void FormSection::setLabel(const std::string& label) { + label_ = label; +} + +const std::string& FormSection::getLabel() const { + return label_; +} + +void FormSection::addTextElement(boost::shared_ptr<FormText>& textElement) { + textElements_.push_back(textElement); +} + +const std::vector<boost::shared_ptr<FormText> >& FormSection::getTextElements() const { + return textElements_; +} + +void FormSection::addReportedRef(boost::shared_ptr<FormReportedRef>& reportedRef) { + reportedRefs_.push_back(reportedRef); +} + +const std::vector<boost::shared_ptr<FormReportedRef> >& FormSection::getReportedRefs() const { + return reportedRefs_; +} + +void FormSection::addChildSection(boost::shared_ptr<FormSection>& childSection) { + childSections_.push_back(childSection); +} + +const std::vector<boost::shared_ptr<FormSection> >& FormSection::getChildSections() const { + return childSections_; +} + +void FormSection::addField(boost::shared_ptr<FormField>& field) { + fields_.push_back(field); +} + +const std::vector<boost::shared_ptr<FormField> >& FormSection::getFields() const { + return fields_; +} + +void FormSection::addFieldRef(std::string ref) { + fieldRefs_.push_back(ref); +} + +const std::vector<std::string> FormSection::getFieldRefs() const { + return fieldRefs_; +} + +}
\ No newline at end of file diff --git a/Swiften/Elements/FormSection.h b/Swiften/Elements/FormSection.h new file mode 100644 index 0000000..f799062 --- /dev/null +++ b/Swiften/Elements/FormSection.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +#pragma once + +#include <string> +#include <vector> +#include <boost/shared_ptr.hpp> +#include <Swiften/Base/API.h> +#include <Swiften/Elements/FormField.h> +#include <Swiften/Elements/FormReportedRef.h> +#include <Swiften/Elements/FormText.h> + +namespace Swift { + + class SWIFTEN_API FormSection { + public: + typedef boost::shared_ptr<FormSection> section; + FormSection(); + ~FormSection(); + void setLabel(const std::string& label); + const std::string& getLabel() const; + void addTextElement(boost::shared_ptr<FormText>& textElement); + const std::vector<boost::shared_ptr<FormText> >& getTextElements() const; + void addReportedRef(boost::shared_ptr<FormReportedRef>& reportedRef); + const std::vector<boost::shared_ptr<FormReportedRef> >& getReportedRefs() const; + void addChildSection(boost::shared_ptr<FormSection>& childSection); + const std::vector<boost::shared_ptr<FormSection> >& getChildSections() const; + void addField(boost::shared_ptr<FormField>& field); + const std::vector<boost::shared_ptr<FormField> >& getFields() const; + void addFieldRef(std::string ref); + const std::vector<std::string> getFieldRefs() const; + + private: + std::string label_; + std::vector<boost::shared_ptr<FormText> > textElements_; + std::vector<boost::shared_ptr<FormReportedRef> > reportedRefs_; + std::vector<boost::shared_ptr<FormSection> > childSections_; + std::vector<boost::shared_ptr<FormField> > fields_; + std::vector<std::string> fieldRefs_; + }; +} diff --git a/Swiften/Elements/FormText.cpp b/Swiften/Elements/FormText.cpp new file mode 100644 index 0000000..cbbfbe4 --- /dev/null +++ b/Swiften/Elements/FormText.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +#include <Swiften/Elements/FormText.h> + +namespace Swift { + +FormText::FormText() { +} + +FormText::~FormText() { +} + +void FormText::setTextString(const std::string& text) { + text_ = text; +} + +const std::string& FormText::getTextString() const { + return text_; +} + +} diff --git a/Swiften/Elements/FormText.h b/Swiften/Elements/FormText.h new file mode 100644 index 0000000..501af11 --- /dev/null +++ b/Swiften/Elements/FormText.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +#pragma once + +#include <string> +#include <boost/shared_ptr.hpp> +#include <Swiften/Base/API.h> + +namespace Swift { + + class SWIFTEN_API FormText{ + + public: + typedef boost::shared_ptr<FormText> text; + FormText(); + virtual ~FormText(); + void setTextString(const std::string& text); + const std::string& getTextString() const; + + private: + std::string text_; + }; +} diff --git a/Swiften/Parser/PayloadParsers/FormParser.cpp b/Swiften/Parser/PayloadParsers/FormParser.cpp index 7422c7b..d400c4c 100644 --- a/Swiften/Parser/PayloadParsers/FormParser.cpp +++ b/Swiften/Parser/PayloadParsers/FormParser.cpp @@ -1,20 +1,21 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ - +#include <iostream> #include <Swiften/Parser/PayloadParsers/FormParser.h> - +#include <map> #include <Swiften/Base/foreach.h> namespace Swift { -FormParser::FormParser() : level_(TopLevel), parsingItem_(false), parsingReported_(false), parsingOption_(false) { +FormParser::FormParser() : level_(TopLevel), parsingItem_(false), parsingReported_(false), parsingOption_(false), parseStarted_(false), hasReportedRef_(false){ } void FormParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) { if (level_ == TopLevel) { + parseStarted_ = true; std::string type = attributes.getAttribute("type"); if (type == "form") { getPayloadInternal()->setType(Form::FormType); @@ -42,6 +43,10 @@ void FormParser::handleStartElement(const std::string& element, const std::strin else if (element == "item") { parsingItem_ = true; } + else if (element == "page") { + currentPage_ = boost::make_shared<FormPage>(); + currentPage_->setLabel(attributes.getAttribute("label")); + } } else if (level_ == FieldLevel && currentField_) { currentText_.clear(); @@ -94,6 +99,30 @@ void FormParser::handleStartElement(const std::string& element, const std::strin currentText_.clear(); } } + if (level_ > PayloadLevel) { + if (element == "section") { + currentSection_ = boost::make_shared<FormSection>(); + currentSection_->setLabel(attributes.getAttribute("label")); + sectionStack_.push_back(currentSection_); + currentSections_.push_back(currentSection_); + } + if (element == "reportedref") { + currentReportedRef_ = boost::make_shared<FormReportedRef>(); + } + if (element == "fieldref") { + currentText_.clear(); + currentFieldRef_ = attributes.getAttribute("var"); + if (sectionStack_.size() > 0) { + sectionStack_.at(sectionStack_.size()-1)->addFieldRef(currentFieldRef_); + } else if (currentPage_) { + currentPage_->addFieldRef(currentFieldRef_); + } + } + if (element == "text") { + currentText_.clear(); + currentTextElement_ = boost::make_shared<FormText>(); + } + } ++level_; } @@ -126,6 +155,10 @@ void FormParser::handleEndElement(const std::string& element, const std::string& getPayloadInternal()->addItem(currentFields_); currentFields_.clear(); } + else if (element == "page") { + getPayloadInternal()->addPage(currentPage_); + currentPages_.push_back(currentPage_); + } } else if (currentField_) { if (element == "required") { @@ -156,11 +189,62 @@ void FormParser::handleEndElement(const std::string& element, const std::string& currentFields_.push_back(currentField_); } else { - getPayloadInternal()->addField(currentField_); + if (currentPages_.size() > 0) { + foreach (boost::shared_ptr<FormPage> page, currentPages_) { + foreach (std::string pRef, page->getFieldRefs()) { + if (pRef == currentField_->getName()) { + page->addField(currentField_); + } + } + } + foreach (boost::shared_ptr<FormSection> section, currentSections_) { + foreach (std::string sRef, section->getFieldRefs()) { + if (sRef == currentField_->getName()) { + section->addField(currentField_); + } + } + } + } else { + getPayloadInternal()->addField(currentField_); + } } currentField_.reset(); } } + if (level_ > PayloadLevel) { + if (element == "section") { + if (sectionStack_.size() > 1) { + // Add the section at the top of the stack to the level below + sectionStack_.at(sectionStack_.size()-2)->addChildSection(sectionStack_.at(sectionStack_.size()-1)); + sectionStack_.pop_back(); + } + else if (sectionStack_.size() == 1) { + // Add the remaining section on the stack to it's parent page + currentPage_->addChildSection(sectionStack_.at(sectionStack_.size()-1)); + sectionStack_.pop_back(); + } + } + if (currentReportedRef_ && !hasReportedRef_) { + if (sectionStack_.size() > 0) { + sectionStack_.at(sectionStack_.size()-1)->addReportedRef(currentReportedRef_); + } else if (currentPage_) { + currentPage_->addReportedRef(currentReportedRef_); + } + hasReportedRef_ = true; + currentReportedRef_.reset(); + } + if (currentTextElement_) { + if (element == "text") { + currentTextElement_->setTextString(currentText_); + } + if (sectionStack_.size() > 0) { + sectionStack_.at(sectionStack_.size()-1)->addTextElement(currentTextElement_); + } else if (currentPage_) { + currentPage_->addTextElement(currentTextElement_); + } + currentTextElement_.reset(); + } + } } void FormParser::handleCharacterData(const std::string& text) { diff --git a/Swiften/Parser/PayloadParsers/FormParser.h b/Swiften/Parser/PayloadParsers/FormParser.h index 85210a5..f865a38 100644 --- a/Swiften/Parser/PayloadParsers/FormParser.h +++ b/Swiften/Parser/PayloadParsers/FormParser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -27,10 +27,20 @@ namespace Swift { std::string currentText_; std::string currentOptionLabel_; std::string currentOptionValue_; + std::string currentFieldRef_; bool parsingItem_; bool parsingReported_; bool parsingOption_; + bool parseStarted_; + bool hasReportedRef_; FormField::ref currentField_; std::vector<FormField::ref> currentFields_; + FormText::text currentTextElement_; + FormReportedRef::ref currentReportedRef_; + FormPage::page currentPage_; + FormSection::section currentSection_; + std::vector<boost::shared_ptr<FormPage> > currentPages_; + std::vector<boost::shared_ptr<FormSection> > sectionStack_; + std::vector<boost::shared_ptr<FormSection> > currentSections_; }; } diff --git a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp index 83fca83..c9e685e 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -15,6 +15,7 @@ using namespace Swift; class FormParserTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(FormParserTest); CPPUNIT_TEST(testParse_FormInformation); + CPPUNIT_TEST(testParse_FormLayout); CPPUNIT_TEST(testParse); CPPUNIT_TEST(testParse_FormItems); CPPUNIT_TEST_SUITE_END(); @@ -37,6 +38,49 @@ class FormParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(Form::SubmitType, payload->getType()); } + void testParse_FormLayout() { + PayloadsParserTester parser; + + // P1 = page one, S1 = section one, F1 = field one, T1 = text one + CPPUNIT_ASSERT(parser.parse( + "<x type=\"form\" xmlns=\"jabber:x:data\">" + "<page label=\"P1\" xmlns=\"http://jabber.org/protocol/xdata-layout\">" + "<reportedref/>" + "<text>P1T1</text>" + "<fieldref var=\"P1F1\"/>" + "<section label=\"P1S1\">" + "<text>P1S1T1</text>" + "<fieldref var=\"P1S1F1\"/>" + "</section>" + "</page>" + "<page label=\"P2\" xmlns=\"http://jabber.org/protocol/xdata-layout\">" + "<section label=\"P2S1\">" + "<section label=\"P2S2\">" + "<section label=\"P2S3\"/>" + "</section>" + "</section>" + "</page>" + "<field label=\"field one\" type=\"text-single\" var=\"P1F1\"/>" + "<field label=\"field two\" type=\"text-single\" var=\"P1S1F1\"/>" + "</x>")); + + Form* payload = dynamic_cast<Form*>(parser.getPayload().get()); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(payload->getFields().size())); + // PAGE ONE - parsing of element types + CPPUNIT_ASSERT_EQUAL(std::string("P1"), payload->getPages()[0]->getLabel()); + CPPUNIT_ASSERT(payload->getPages()[0]->getReportedRefs()[0]); + CPPUNIT_ASSERT_EQUAL(std::string("P1T1"), payload->getPages()[0]->getTextElements()[0]->getTextString()); + CPPUNIT_ASSERT_EQUAL(std::string("P1F1"), payload->getPages()[0]->getFields()[0]->getName()); + CPPUNIT_ASSERT_EQUAL(std::string("P1S1"), payload->getPages()[0]->getChildSections()[0]->getLabel()); + CPPUNIT_ASSERT_EQUAL(std::string("P1S1T1"), payload->getPages()[0]->getChildSections()[0]->getTextElements()[0]->getTextString()); + CPPUNIT_ASSERT_EQUAL(std::string("P1S1F1"), payload->getPages()[0]->getChildSections()[0]->getFields()[0]->getName()); + // PAGE TWO - parsing of nested elements + CPPUNIT_ASSERT_EQUAL(std::string("P2"), payload->getPages()[1]->getLabel()); + CPPUNIT_ASSERT_EQUAL(std::string("P2S1"), payload->getPages()[1]->getChildSections()[0]->getLabel()); + CPPUNIT_ASSERT_EQUAL(std::string("P2S2"), payload->getPages()[1]->getChildSections()[0]->getChildSections()[0]->getLabel()); + CPPUNIT_ASSERT_EQUAL(std::string("P2S3"), payload->getPages()[1]->getChildSections()[0]->getChildSections()[0]->getChildSections()[0]->getLabel()); + } + void testParse() { PayloadsParserTester parser; diff --git a/Swiften/SConscript b/Swiften/SConscript index 340688b..d1709dc 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -125,6 +125,9 @@ if env["SCONS_STAGE"] == "build" : "Elements/Presence.cpp", "Elements/Form.cpp", "Elements/FormField.cpp", + "Elements/FormPage.cpp", + "Elements/FormSection.cpp", + "Elements/FormText.cpp", "Elements/StreamFeatures.cpp", "Elements/Element.cpp", "Elements/ToplevelElement.cpp", diff --git a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp index a343989..633ead6 100644 --- a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -30,11 +30,9 @@ namespace { } } - namespace Swift { -FormSerializer::FormSerializer() : GenericPayloadSerializer<Form>() { -} +FormSerializer::FormSerializer() : GenericPayloadSerializer<Form>() {} std::string FormSerializer::serializePayload(boost::shared_ptr<Form> form) const { if (!form) { @@ -56,6 +54,9 @@ std::string FormSerializer::serializePayload(boost::shared_ptr<Form> form) cons if (!form->getInstructions().empty()) { multiLineify(form->getInstructions(), "instructions", formElement); } + foreach(boost::shared_ptr<FormPage> page, form->getPages()) { + formElement->addNode(pageToXML(page)); + } foreach(boost::shared_ptr<FormField> field, form->getFields()) { formElement->addNode(fieldToXML(field, true)); } @@ -66,6 +67,7 @@ std::string FormSerializer::serializePayload(boost::shared_ptr<Form> form) cons } formElement->addNode(reportedElement); } + foreach(Form::FormItem item, form->getItems()) { boost::shared_ptr<XMLElement> itemElement(new XMLElement("item")); foreach(FormField::ref field, item) { @@ -74,9 +76,72 @@ std::string FormSerializer::serializePayload(boost::shared_ptr<Form> form) cons formElement->addNode(itemElement); } + foreach(const FormText::text text, form->getTextElements()) { + formElement->addNode(textToXML(text)); + } + + foreach (boost::shared_ptr<FormField> field, fields_) { + formElement->addNode(fieldToXML(field,true)); + } + return formElement->serialize(); } +boost::shared_ptr<XMLElement> FormSerializer::textToXML(boost::shared_ptr<FormText> text) const { + boost::shared_ptr<XMLElement> textElement (new XMLElement("text")); + textElement->addNode(boost::make_shared<XMLTextNode>(text->getTextString())); + return textElement; +} + +boost::shared_ptr<XMLElement> FormSerializer::fieldRefToXML(const std::string& ref) const { + boost::shared_ptr<XMLElement> fieldRefElement(new XMLElement("fieldref")); + fieldRefElement->setAttribute("var", ref); + return fieldRefElement; +} + +boost::shared_ptr<XMLElement> FormSerializer::pageToXML(boost::shared_ptr<FormPage> page) const { + boost::shared_ptr<XMLElement> pageElement(new XMLElement("page")); + pageElement->setAttribute("xmlns", page->getXMLNS()); + if (!page->getLabel().empty()) { + pageElement->setAttribute("label", page->getLabel()); + } + foreach(const FormText::text text, page->getTextElements()) { + pageElement->addNode(textToXML(text)); + } + foreach (const boost::shared_ptr<FormField> field, page->getFields()) { + pageElement->addNode(fieldRefToXML(field->getName())); + fields_.push_back(field); + } + foreach(const FormReportedRef::ref reportedRef, page->getReportedRefs()) { + pageElement->addNode(boost::make_shared<XMLElement>("reportedref")); + } + foreach(const FormSection::section section, page->getChildSections()) { + pageElement->addNode(sectionToXML(section)); + } + return pageElement; +} + +boost::shared_ptr<XMLElement> FormSerializer::sectionToXML(boost::shared_ptr<FormSection> section) const { + boost::shared_ptr<XMLElement> sectionElement(new XMLElement("section")); + if (!section->getLabel().empty()) { + sectionElement->setAttribute("label", section->getLabel()); + } + foreach(const FormText::text text, section->getTextElements()) { + sectionElement->addNode(textToXML(text)); + } + foreach(const boost::shared_ptr<FormField> field, section->getFields()) { + sectionElement->addNode(fieldRefToXML(field->getName())); + fields_.push_back(field); + } + foreach(const FormReportedRef::ref reportedRef, section->getReportedRefs()) { + sectionElement->addNode(boost::make_shared<XMLElement>("reportedref")); + } + foreach(const FormSection::section childSection, section->getChildSections()) { + sectionElement->addNode(sectionToXML(childSection)); + } + return sectionElement; +} + boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormField> field, bool withTypeAttribute) const { boost::shared_ptr<XMLElement> fieldElement(new XMLElement("field")); if (!field->getName().empty()) { @@ -127,10 +192,8 @@ boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormF boost::shared_ptr<XMLElement> valueElement(new XMLElement("value")); valueElement->addNode(XMLTextNode::create(option.value)); optionElement->addNode(valueElement); - fieldElement->addNode(optionElement); } - return fieldElement; } @@ -144,5 +207,4 @@ void FormSerializer::multiLineify(const std::string& text, const std::string& el element->addNode(lineElement); } } - } diff --git a/Swiften/Serializer/PayloadSerializers/FormSerializer.h b/Swiften/Serializer/PayloadSerializers/FormSerializer.h index 88f2a43..ad3f472 100644 --- a/Swiften/Serializer/PayloadSerializers/FormSerializer.h +++ b/Swiften/Serializer/PayloadSerializers/FormSerializer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -10,19 +10,24 @@ #include <Swiften/Serializer/GenericPayloadSerializer.h> #include <Swiften/Elements/Form.h> #include <Swiften/Elements/FormField.h> +#include <Swiften/Elements/FormPage.h> +#include <Swiften/Elements/FormSection.h> #include <Swiften/Serializer/XML/XMLElement.h> namespace Swift { class SWIFTEN_API FormSerializer : public GenericPayloadSerializer<Form> { public: FormSerializer(); - virtual std::string serializePayload(boost::shared_ptr<Form>) const; private: + boost::shared_ptr<XMLElement> textToXML(boost::shared_ptr<FormText> textElement) const; + boost::shared_ptr<XMLElement> fieldRefToXML(const std::string& ref) const; + boost::shared_ptr<XMLElement> reportedRefToXML(boost::shared_ptr<FormReportedRef> reportedRef) const; + boost::shared_ptr<XMLElement> pageToXML(boost::shared_ptr<FormPage> page) const; + boost::shared_ptr<XMLElement> sectionToXML(boost::shared_ptr<FormSection> section) const; boost::shared_ptr<XMLElement> fieldToXML(boost::shared_ptr<FormField> field, bool withTypeAttribute) const; void multiLineify(const std::string& text, const std::string& elementName, boost::shared_ptr<XMLElement> parent) const; + mutable std::vector<boost::shared_ptr<FormField> > fields_; }; } - - diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp index 887edc1..73c9db1 100644 --- a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -14,6 +14,7 @@ using namespace Swift; class FormSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(FormSerializerTest); CPPUNIT_TEST(testSerializeFormInformation); + CPPUNIT_TEST(testSerializeLayout); CPPUNIT_TEST(testSerializeFields); CPPUNIT_TEST(testSerializeFormItems); CPPUNIT_TEST_SUITE_END(); @@ -33,6 +34,71 @@ class FormSerializerTest : public CppUnit::TestFixture { "</x>"), testling.serialize(form)); } + void testSerializeLayout() { + FormSerializer testling; + boost::shared_ptr<Form> form(new Form(Form::FormType)); + + FormPage::page page = boost::make_shared<FormPage>(); + page->setLabel("P1"); + FormReportedRef::ref reportedRef = boost::make_shared<FormReportedRef>(); + page->addReportedRef(reportedRef); + FormText::text formText = boost::make_shared<FormText>(); + formText->setTextString("P1T1"); + page->addTextElement(formText); + FormField::ref field = boost::make_shared<FormField>(FormField::TextSingleType); + field->setName("P1F1"); + field->setLabel("field one"); + page->addField(field); + + FormSection::section section = boost::make_shared<FormSection>(); + section->setLabel("P1S1"); + formText = boost::make_shared<FormText>(); + formText->setTextString("P1S1T1"); + section->addTextElement(formText); + field = boost::make_shared<FormField>(FormField::TextSingleType); + field->setName("P1S1F1"); + field->setLabel("field two"); + section->addField(field); + page->addChildSection(section); + form->addPage(page); + + page = boost::make_shared<FormPage>(); + page->setLabel("P2"); + section = boost::make_shared<FormSection>(); + section->setLabel("P2S1"); + FormSection::section subSection = boost::make_shared<FormSection>(); + subSection->setLabel("P2S2"); + FormSection::section subSection2 = boost::make_shared<FormSection>(); + subSection2->setLabel("P2S3"); + subSection->addChildSection(subSection2); + section->addChildSection(subSection); + page->addChildSection(section); + form->addPage(page); + + // P1 = page one, S1 = section one, F1 = field one, T1 = text one + CPPUNIT_ASSERT_EQUAL(std::string( + "<x type=\"form\" xmlns=\"jabber:x:data\">" + "<page label=\"P1\" xmlns=\"http://jabber.org/protocol/xdata-layout\">" + "<text>P1T1</text>" + "<fieldref var=\"P1F1\"/>" + "<reportedref/>" + "<section label=\"P1S1\">" + "<text>P1S1T1</text>" + "<fieldref var=\"P1S1F1\"/>" + "</section>" + "</page>" + "<page label=\"P2\" xmlns=\"http://jabber.org/protocol/xdata-layout\">" + "<section label=\"P2S1\">" + "<section label=\"P2S2\">" + "<section label=\"P2S3\"/>" + "</section>" + "</section>" + "</page>" + "<field label=\"field one\" type=\"text-single\" var=\"P1F1\"/>" + "<field label=\"field two\" type=\"text-single\" var=\"P1S1F1\"/>" + "</x>"), testling.serialize(form)); + } + void testSerializeFields() { FormSerializer testling; boost::shared_ptr<Form> form(new Form(Form::FormType)); @@ -140,7 +206,6 @@ class FormSerializerTest : public CppUnit::TestFixture { FormSerializer testling; boost::shared_ptr<Form> form(new Form(Form::ResultType)); - FormField::ref field = boost::make_shared<FormField>(FormField::HiddenType, "jabber:iq:search"); field->setName("FORM_TYPE"); form->addField(field); |