Skip to content

Commit

Permalink
56 incorrect validation function generated for optional choices (#57)
Browse files Browse the repository at this point in the history
* adding testcase for optional choices
* fixed ValidateChoices function for optional choices
  • Loading branch information
RlanderRISCSW authored Jan 17, 2024
1 parent 579cdfd commit 5661239
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 4 deletions.
44 changes: 40 additions & 4 deletions src/lib/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Scope> 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<std::size_t>& 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 << ")";
}
Expand Down
14 changes: 14 additions & 0 deletions src/lib/SchemaParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ namespace tigl {
// </choice>
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));
});
Expand Down
1 change: 1 addition & 0 deletions src/lib/SchemaParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ namespace tigl {

struct Choice : XSDElement {
std::vector<Variant<Element, Group, Choice, Sequence, Any>> elements;
unsigned int minOccurs;
};

struct SimpleContent : XSDElement {
Expand Down
1 change: 1 addition & 0 deletions src/lib/TypeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/lib/TypeSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ namespace tigl {
using ChoiceElements = std::vector<boost::variant<ChoiceElement, boost::recursive_wrapper<Choice>>>;

struct Choice {
unsigned int minOccurs;
std::vector<ChoiceElements> options;
};

Expand Down
217 changes: 217 additions & 0 deletions test/data/optionalchoice/ref.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
#include <string>
#include <tixi.h>
#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<int>& GetA_choice1() const;
TIGL_EXPORT virtual void SetA_choice1(const boost::optional<int>& value);

TIGL_EXPORT virtual const boost::optional<int>& GetB_choice2() const;
TIGL_EXPORT virtual void SetB_choice2(const boost::optional<int>& value);

protected:
boost::optional<int> m_a_choice1;
boost::optional<int> 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<int>(tixiHandle, xpath + "/a");
}

// read element b
if (tixi::TixiCheckElement(tixiHandle, xpath + "/b")) {
m_b_choice2 = tixi::TixiGetElement<int>(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<int>& CPACSRoot::GetA_choice1() const
{
return m_a_choice1;
}

void CPACSRoot::SetA_choice1(const boost::optional<int>& value)
{
m_a_choice1 = value;
}

const boost::optional<int>& CPACSRoot::GetB_choice2() const
{
return m_b_choice2;
}

void CPACSRoot::SetB_choice2(const boost::optional<int>& value)
{
m_b_choice2 = value;
}

} // namespace generated
} // namespace tigl
10 changes: 10 additions & 0 deletions test/data/optionalchoice/schema.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="root" type="RootType"/>
<xsd:complexType name="RootType">
<xsd:choice minOccurs="0">
<xsd:element name="a" type="xsd:integer"/>
<xsd:element name="b" type="xsd:integer"/>
</xsd:choice>
</xsd:complexType>
</xsd:schema>
4 changes: 4 additions & 0 deletions test/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ BOOST_AUTO_TEST_CASE(complextypewithsimplecontent) {
BOOST_AUTO_TEST_CASE(collapsedifferentenums) {
runTest();
}

BOOST_AUTO_TEST_CASE(optionalchoice) {
runTest();
}

0 comments on commit 5661239

Please sign in to comment.