Skip to content

Commit 5661239

Browse files
56 incorrect validation function generated for optional choices (RISCSoftware#57)
* adding testcase for optional choices * fixed ValidateChoices function for optional choices
1 parent 579cdfd commit 5661239

File tree

8 files changed

+288
-4
lines changed

8 files changed

+288
-4
lines changed

src/lib/CodeGen.cpp

+40-4
Original file line numberDiff line numberDiff line change
@@ -1063,16 +1063,52 @@ namespace tigl {
10631063
cpp << "true // " << f.fieldName() << " is optional in choice";
10641064
}
10651065

1066-
void operator()(const Choice& c) {
1066+
void operator()(const Choice& ch) {
10671067
cpp << "(";
10681068
{
10691069
Scope s(cpp);
1070-
for (const auto& cc : c.options) {
1071-
(*this)(cc, c);
1072-
if (&cc != &c.options.back())
1070+
boost::optional<Scope> additionalScope;
1071+
1072+
if (ch.minOccurs == 0) {
1073+
cpp << "// all uninitialized is valid since choice is optional!";
1074+
cpp << "!(";
1075+
{
1076+
Scope s(cpp);
1077+
1078+
RecursiveColletor parentCollector;
1079+
parentCollector(ch);
1080+
auto& allIndices = parentCollector.indices;
1081+
1082+
auto unique = [](std::vector<std::size_t>& v) {
1083+
std::sort(std::begin(v), std::end(v));
1084+
const auto it = std::unique(std::begin(v), std::end(v));
1085+
v.erase(it, std::end(v));
1086+
};
1087+
unique(allIndices);
1088+
1089+
for (const auto& i : allIndices) {
1090+
writeIsFieldThere(cpp, c.fields[i]);
1091+
if (&i != &allIndices.back())
1092+
cpp << "||";
1093+
}
1094+
}
1095+
cpp << ")";
1096+
cpp << "||";
1097+
cpp << "(";
1098+
additionalScope.emplace(cpp);
1099+
}
1100+
1101+
for (const auto &ces : ch.options) {
1102+
(*this)(ces, ch);
1103+
if (&ces != &ch.options.back())
10731104
cpp << "+";
10741105
}
10751106
cpp << "== 1";
1107+
1108+
if (additionalScope) {
1109+
additionalScope.reset();
1110+
cpp << ")";
1111+
}
10761112
}
10771113
cpp << ")";
10781114
}

src/lib/SchemaParser.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ namespace tigl {
6464
// </choice>
6565
Choice ch;
6666
ch.xpath = xpath;
67+
68+
// minOccurs
69+
if (!document.checkAttribute(xpath, "minOccurs"))
70+
ch.minOccurs = 1;
71+
else {
72+
const auto minOccurs = document.textAttribute(xpath, "minOccurs");
73+
const auto minOccursInt = std::stoi(minOccurs);
74+
if (minOccursInt < 0)
75+
throw std::runtime_error("minOccurs is negative: " + xpath);
76+
else if (minOccursInt > 1)
77+
throw std::runtime_error("support for minOccurs>1 not implemented for choices yet: " + xpath);
78+
ch.minOccurs = minOccursInt;
79+
}
80+
6781
document.forEachChild(xpath, "xsd:element", [&](const std::string& xpath) {
6882
ch.elements.push_back(readElement(xpath, containingTypeName));
6983
});

src/lib/SchemaParser.h

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ namespace tigl {
5454

5555
struct Choice : XSDElement {
5656
std::vector<Variant<Element, Group, Choice, Sequence, Any>> elements;
57+
unsigned int minOccurs;
5758
};
5859

5960
struct SimpleContent : XSDElement {

src/lib/TypeSystem.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ namespace tigl {
117117
const auto countBefore = members.size();
118118

119119
Choice choice;
120+
choice.minOccurs = c.minOccurs;
120121
for (const auto& v : c.elements | boost::adaptors::indexed(1)) {
121122
// collect members of one choice
122123
auto indices = choiceIndices;

src/lib/TypeSystem.h

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ namespace tigl {
103103
using ChoiceElements = std::vector<boost::variant<ChoiceElement, boost::recursive_wrapper<Choice>>>;
104104

105105
struct Choice {
106+
unsigned int minOccurs;
106107
std::vector<ChoiceElements> options;
107108
};
108109

test/data/optionalchoice/ref.cpp

+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// Copyright (c) 2020 RISC Software GmbH
2+
//
3+
// This file was generated by CPACSGen from CPACS XML Schema (c) German Aerospace Center (DLR/SC).
4+
// Do not edit, all changes are lost when files are re-generated.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License")
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
#pragma once
19+
20+
#include <boost/optional.hpp>
21+
#include <boost/utility/in_place_factory.hpp>
22+
#include <string>
23+
#include <tixi.h>
24+
#include "tigl_internal.h"
25+
26+
namespace tigl
27+
{
28+
class CTiglUIDObject;
29+
30+
namespace generated
31+
{
32+
// This class is used in:
33+
class CPACSRoot
34+
{
35+
public:
36+
TIGL_EXPORT CPACSRoot();
37+
TIGL_EXPORT virtual ~CPACSRoot();
38+
39+
TIGL_EXPORT virtual CTiglUIDObject* GetNextUIDParent();
40+
TIGL_EXPORT virtual const CTiglUIDObject* GetNextUIDParent() const;
41+
42+
TIGL_EXPORT virtual void ReadCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath);
43+
TIGL_EXPORT virtual void WriteCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath) const;
44+
45+
TIGL_EXPORT bool ValidateChoices() const;
46+
47+
TIGL_EXPORT virtual const boost::optional<int>& GetA_choice1() const;
48+
TIGL_EXPORT virtual void SetA_choice1(const boost::optional<int>& value);
49+
50+
TIGL_EXPORT virtual const boost::optional<int>& GetB_choice2() const;
51+
TIGL_EXPORT virtual void SetB_choice2(const boost::optional<int>& value);
52+
53+
protected:
54+
boost::optional<int> m_a_choice1;
55+
boost::optional<int> m_b_choice2;
56+
57+
private:
58+
CPACSRoot(const CPACSRoot&) = delete;
59+
CPACSRoot& operator=(const CPACSRoot&) = delete;
60+
61+
CPACSRoot(CPACSRoot&&) = delete;
62+
CPACSRoot& operator=(CPACSRoot&&) = delete;
63+
};
64+
} // namespace generated
65+
66+
// Aliases in tigl namespace
67+
using CCPACSRoot = generated::CPACSRoot;
68+
} // namespace tigl
69+
// Copyright (c) 2020 RISC Software GmbH
70+
//
71+
// This file was generated by CPACSGen from CPACS XML Schema (c) German Aerospace Center (DLR/SC).
72+
// Do not edit, all changes are lost when files are re-generated.
73+
//
74+
// Licensed under the Apache License, Version 2.0 (the "License")
75+
// you may not use this file except in compliance with the License.
76+
// You may obtain a copy of the License at
77+
//
78+
// http://www.apache.org/licenses/LICENSE-2.0
79+
//
80+
// Unless required by applicable law or agreed to in writing, software
81+
// distributed under the License is distributed on an "AS IS" BASIS,
82+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
83+
// See the License for the specific language governing permissions and
84+
// limitations under the License.
85+
86+
#include "CPACSRoot.h"
87+
#include "CTiglError.h"
88+
#include "CTiglLogging.h"
89+
#include "CTiglUIDObject.h"
90+
#include "TixiHelper.h"
91+
92+
namespace tigl
93+
{
94+
namespace generated
95+
{
96+
CPACSRoot::CPACSRoot()
97+
{
98+
}
99+
100+
CPACSRoot::~CPACSRoot()
101+
{
102+
}
103+
104+
const CTiglUIDObject* CPACSRoot::GetNextUIDParent() const
105+
{
106+
return nullptr;
107+
}
108+
109+
CTiglUIDObject* CPACSRoot::GetNextUIDParent()
110+
{
111+
return nullptr;
112+
}
113+
114+
void CPACSRoot::ReadCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath)
115+
{
116+
// read element a
117+
if (tixi::TixiCheckElement(tixiHandle, xpath + "/a")) {
118+
m_a_choice1 = tixi::TixiGetElement<int>(tixiHandle, xpath + "/a");
119+
}
120+
121+
// read element b
122+
if (tixi::TixiCheckElement(tixiHandle, xpath + "/b")) {
123+
m_b_choice2 = tixi::TixiGetElement<int>(tixiHandle, xpath + "/b");
124+
}
125+
126+
if (!ValidateChoices()) {
127+
LOG(ERROR) << "Invalid choice configuration at xpath " << xpath;
128+
}
129+
}
130+
131+
void CPACSRoot::WriteCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath) const
132+
{
133+
// write element a
134+
if (m_a_choice1) {
135+
tixi::TixiCreateElementIfNotExists(tixiHandle, xpath + "/a");
136+
tixi::TixiSaveElement(tixiHandle, xpath + "/a", *m_a_choice1);
137+
}
138+
else {
139+
if (tixi::TixiCheckElement(tixiHandle, xpath + "/a")) {
140+
tixi::TixiRemoveElement(tixiHandle, xpath + "/a");
141+
}
142+
}
143+
144+
// write element b
145+
if (m_b_choice2) {
146+
tixi::TixiCreateElementIfNotExists(tixiHandle, xpath + "/b");
147+
tixi::TixiSaveElement(tixiHandle, xpath + "/b", *m_b_choice2);
148+
}
149+
else {
150+
if (tixi::TixiCheckElement(tixiHandle, xpath + "/b")) {
151+
tixi::TixiRemoveElement(tixiHandle, xpath + "/b");
152+
}
153+
}
154+
155+
}
156+
157+
bool CPACSRoot::ValidateChoices() const
158+
{
159+
return
160+
(
161+
(
162+
// all uninitialized is valid since choice is optional!
163+
!(
164+
m_a_choice1.is_initialized()
165+
||
166+
m_b_choice2.is_initialized()
167+
)
168+
||
169+
(
170+
(
171+
// mandatory elements of this choice must be there
172+
m_a_choice1.is_initialized()
173+
&&
174+
// elements of other choices must not be there
175+
!(
176+
m_b_choice2.is_initialized()
177+
)
178+
)
179+
+
180+
(
181+
// mandatory elements of this choice must be there
182+
m_b_choice2.is_initialized()
183+
&&
184+
// elements of other choices must not be there
185+
!(
186+
m_a_choice1.is_initialized()
187+
)
188+
)
189+
== 1
190+
)
191+
)
192+
)
193+
;
194+
}
195+
196+
const boost::optional<int>& CPACSRoot::GetA_choice1() const
197+
{
198+
return m_a_choice1;
199+
}
200+
201+
void CPACSRoot::SetA_choice1(const boost::optional<int>& value)
202+
{
203+
m_a_choice1 = value;
204+
}
205+
206+
const boost::optional<int>& CPACSRoot::GetB_choice2() const
207+
{
208+
return m_b_choice2;
209+
}
210+
211+
void CPACSRoot::SetB_choice2(const boost::optional<int>& value)
212+
{
213+
m_b_choice2 = value;
214+
}
215+
216+
} // namespace generated
217+
} // namespace tigl

test/data/optionalchoice/schema.xsd

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3+
<xsd:element name="root" type="RootType"/>
4+
<xsd:complexType name="RootType">
5+
<xsd:choice minOccurs="0">
6+
<xsd:element name="a" type="xsd:integer"/>
7+
<xsd:element name="b" type="xsd:integer"/>
8+
</xsd:choice>
9+
</xsd:complexType>
10+
</xsd:schema>

test/main.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,7 @@ BOOST_AUTO_TEST_CASE(complextypewithsimplecontent) {
8080
BOOST_AUTO_TEST_CASE(collapsedifferentenums) {
8181
runTest();
8282
}
83+
84+
BOOST_AUTO_TEST_CASE(optionalchoice) {
85+
runTest();
86+
}

0 commit comments

Comments
 (0)