From 5661239f06a7436df24be5b07e75f384b609ec5e Mon Sep 17 00:00:00 2001 From: RlanderRISCSW Date: Wed, 17 Jan 2024 11:31:45 +0100 Subject: [PATCH] 56 incorrect validation function generated for optional choices (#57) * adding testcase for optional choices * fixed ValidateChoices function for optional choices --- src/lib/CodeGen.cpp | 44 +++++- src/lib/SchemaParser.cpp | 14 ++ src/lib/SchemaParser.h | 1 + src/lib/TypeSystem.cpp | 1 + src/lib/TypeSystem.h | 1 + test/data/optionalchoice/ref.cpp | 217 ++++++++++++++++++++++++++++ test/data/optionalchoice/schema.xsd | 10 ++ test/main.cpp | 4 + 8 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 test/data/optionalchoice/ref.cpp create mode 100644 test/data/optionalchoice/schema.xsd diff --git a/src/lib/CodeGen.cpp b/src/lib/CodeGen.cpp index 055e07a..fcafe43 100644 --- a/src/lib/CodeGen.cpp +++ b/src/lib/CodeGen.cpp @@ -1063,16 +1063,52 @@ namespace tigl { cpp << "true // " << f.fieldName() << " is optional in choice"; } - void operator()(const Choice& c) { + void operator()(const Choice& ch) { cpp << "("; { Scope s(cpp); - for (const auto& cc : c.options) { - (*this)(cc, c); - if (&cc != &c.options.back()) + boost::optional additionalScope; + + if (ch.minOccurs == 0) { + cpp << "// all uninitialized is valid since choice is optional!"; + cpp << "!("; + { + Scope s(cpp); + + RecursiveColletor parentCollector; + parentCollector(ch); + auto& allIndices = parentCollector.indices; + + auto unique = [](std::vector& v) { + std::sort(std::begin(v), std::end(v)); + const auto it = std::unique(std::begin(v), std::end(v)); + v.erase(it, std::end(v)); + }; + unique(allIndices); + + for (const auto& i : allIndices) { + writeIsFieldThere(cpp, c.fields[i]); + if (&i != &allIndices.back()) + cpp << "||"; + } + } + cpp << ")"; + cpp << "||"; + cpp << "("; + additionalScope.emplace(cpp); + } + + for (const auto &ces : ch.options) { + (*this)(ces, ch); + if (&ces != &ch.options.back()) cpp << "+"; } cpp << "== 1"; + + if (additionalScope) { + additionalScope.reset(); + cpp << ")"; + } } cpp << ")"; } diff --git a/src/lib/SchemaParser.cpp b/src/lib/SchemaParser.cpp index 25ea9bc..31737ca 100644 --- a/src/lib/SchemaParser.cpp +++ b/src/lib/SchemaParser.cpp @@ -64,6 +64,20 @@ namespace tigl { // Choice ch; ch.xpath = xpath; + + // minOccurs + if (!document.checkAttribute(xpath, "minOccurs")) + ch.minOccurs = 1; + else { + const auto minOccurs = document.textAttribute(xpath, "minOccurs"); + const auto minOccursInt = std::stoi(minOccurs); + if (minOccursInt < 0) + throw std::runtime_error("minOccurs is negative: " + xpath); + else if (minOccursInt > 1) + throw std::runtime_error("support for minOccurs>1 not implemented for choices yet: " + xpath); + ch.minOccurs = minOccursInt; + } + document.forEachChild(xpath, "xsd:element", [&](const std::string& xpath) { ch.elements.push_back(readElement(xpath, containingTypeName)); }); diff --git a/src/lib/SchemaParser.h b/src/lib/SchemaParser.h index 4b6b20c..5512705 100644 --- a/src/lib/SchemaParser.h +++ b/src/lib/SchemaParser.h @@ -54,6 +54,7 @@ namespace tigl { struct Choice : XSDElement { std::vector> elements; + unsigned int minOccurs; }; struct SimpleContent : XSDElement { diff --git a/src/lib/TypeSystem.cpp b/src/lib/TypeSystem.cpp index 6f90810..fb9c160 100644 --- a/src/lib/TypeSystem.cpp +++ b/src/lib/TypeSystem.cpp @@ -117,6 +117,7 @@ namespace tigl { const auto countBefore = members.size(); Choice choice; + choice.minOccurs = c.minOccurs; for (const auto& v : c.elements | boost::adaptors::indexed(1)) { // collect members of one choice auto indices = choiceIndices; diff --git a/src/lib/TypeSystem.h b/src/lib/TypeSystem.h index cfd228c..d578fa2 100644 --- a/src/lib/TypeSystem.h +++ b/src/lib/TypeSystem.h @@ -103,6 +103,7 @@ namespace tigl { using ChoiceElements = std::vector>>; struct Choice { + unsigned int minOccurs; std::vector options; }; diff --git a/test/data/optionalchoice/ref.cpp b/test/data/optionalchoice/ref.cpp new file mode 100644 index 0000000..aefd1b3 --- /dev/null +++ b/test/data/optionalchoice/ref.cpp @@ -0,0 +1,217 @@ +// Copyright (c) 2020 RISC Software GmbH +// +// This file was generated by CPACSGen from CPACS XML Schema (c) German Aerospace Center (DLR/SC). +// Do not edit, all changes are lost when files are re-generated. +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include +#include "tigl_internal.h" + +namespace tigl +{ +class CTiglUIDObject; + +namespace generated +{ + // This class is used in: + class CPACSRoot + { + public: + TIGL_EXPORT CPACSRoot(); + TIGL_EXPORT virtual ~CPACSRoot(); + + TIGL_EXPORT virtual CTiglUIDObject* GetNextUIDParent(); + TIGL_EXPORT virtual const CTiglUIDObject* GetNextUIDParent() const; + + TIGL_EXPORT virtual void ReadCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath); + TIGL_EXPORT virtual void WriteCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath) const; + + TIGL_EXPORT bool ValidateChoices() const; + + TIGL_EXPORT virtual const boost::optional& GetA_choice1() const; + TIGL_EXPORT virtual void SetA_choice1(const boost::optional& value); + + TIGL_EXPORT virtual const boost::optional& GetB_choice2() const; + TIGL_EXPORT virtual void SetB_choice2(const boost::optional& value); + + protected: + boost::optional m_a_choice1; + boost::optional m_b_choice2; + + private: + CPACSRoot(const CPACSRoot&) = delete; + CPACSRoot& operator=(const CPACSRoot&) = delete; + + CPACSRoot(CPACSRoot&&) = delete; + CPACSRoot& operator=(CPACSRoot&&) = delete; + }; +} // namespace generated + +// Aliases in tigl namespace +using CCPACSRoot = generated::CPACSRoot; +} // namespace tigl +// Copyright (c) 2020 RISC Software GmbH +// +// This file was generated by CPACSGen from CPACS XML Schema (c) German Aerospace Center (DLR/SC). +// Do not edit, all changes are lost when files are re-generated. +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "CPACSRoot.h" +#include "CTiglError.h" +#include "CTiglLogging.h" +#include "CTiglUIDObject.h" +#include "TixiHelper.h" + +namespace tigl +{ +namespace generated +{ + CPACSRoot::CPACSRoot() + { + } + + CPACSRoot::~CPACSRoot() + { + } + + const CTiglUIDObject* CPACSRoot::GetNextUIDParent() const + { + return nullptr; + } + + CTiglUIDObject* CPACSRoot::GetNextUIDParent() + { + return nullptr; + } + + void CPACSRoot::ReadCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath) + { + // read element a + if (tixi::TixiCheckElement(tixiHandle, xpath + "/a")) { + m_a_choice1 = tixi::TixiGetElement(tixiHandle, xpath + "/a"); + } + + // read element b + if (tixi::TixiCheckElement(tixiHandle, xpath + "/b")) { + m_b_choice2 = tixi::TixiGetElement(tixiHandle, xpath + "/b"); + } + + if (!ValidateChoices()) { + LOG(ERROR) << "Invalid choice configuration at xpath " << xpath; + } + } + + void CPACSRoot::WriteCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath) const + { + // write element a + if (m_a_choice1) { + tixi::TixiCreateElementIfNotExists(tixiHandle, xpath + "/a"); + tixi::TixiSaveElement(tixiHandle, xpath + "/a", *m_a_choice1); + } + else { + if (tixi::TixiCheckElement(tixiHandle, xpath + "/a")) { + tixi::TixiRemoveElement(tixiHandle, xpath + "/a"); + } + } + + // write element b + if (m_b_choice2) { + tixi::TixiCreateElementIfNotExists(tixiHandle, xpath + "/b"); + tixi::TixiSaveElement(tixiHandle, xpath + "/b", *m_b_choice2); + } + else { + if (tixi::TixiCheckElement(tixiHandle, xpath + "/b")) { + tixi::TixiRemoveElement(tixiHandle, xpath + "/b"); + } + } + + } + + bool CPACSRoot::ValidateChoices() const + { + return + ( + ( + // all uninitialized is valid since choice is optional! + !( + m_a_choice1.is_initialized() + || + m_b_choice2.is_initialized() + ) + || + ( + ( + // mandatory elements of this choice must be there + m_a_choice1.is_initialized() + && + // elements of other choices must not be there + !( + m_b_choice2.is_initialized() + ) + ) + + + ( + // mandatory elements of this choice must be there + m_b_choice2.is_initialized() + && + // elements of other choices must not be there + !( + m_a_choice1.is_initialized() + ) + ) + == 1 + ) + ) + ) + ; + } + + const boost::optional& CPACSRoot::GetA_choice1() const + { + return m_a_choice1; + } + + void CPACSRoot::SetA_choice1(const boost::optional& value) + { + m_a_choice1 = value; + } + + const boost::optional& CPACSRoot::GetB_choice2() const + { + return m_b_choice2; + } + + void CPACSRoot::SetB_choice2(const boost::optional& value) + { + m_b_choice2 = value; + } + +} // namespace generated +} // namespace tigl diff --git a/test/data/optionalchoice/schema.xsd b/test/data/optionalchoice/schema.xsd new file mode 100644 index 0000000..fc4f59e --- /dev/null +++ b/test/data/optionalchoice/schema.xsd @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp index aed9c9e..eae08d4 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -80,3 +80,7 @@ BOOST_AUTO_TEST_CASE(complextypewithsimplecontent) { BOOST_AUTO_TEST_CASE(collapsedifferentenums) { runTest(); } + +BOOST_AUTO_TEST_CASE(optionalchoice) { + runTest(); +}