Fix xsd generation in ddsconf

Signed-off-by: Jeroen Koekkoek <jeroen@koekkoek.nl>
This commit is contained in:
Jeroen Koekkoek 2020-06-26 09:32:24 +02:00
parent 93c75186f0
commit f4e99f41f6
13 changed files with 132 additions and 101 deletions

View file

@ -192,7 +192,6 @@ script:
-DENABLE_LIFESPAN=${LIFESPAN} -DENABLE_LIFESPAN=${LIFESPAN}
-DENABLE_DEADLINE_MISSED=${DEADLINE} -DENABLE_DEADLINE_MISSED=${DEADLINE}
-DBUILD_TESTING=on -DBUILD_TESTING=on
-DBUILD_SCHEMA=on
-DWERROR=on -DWERROR=on
-G "${GENERATOR}" .. -G "${GENERATOR}" ..
- | - |
@ -219,8 +218,6 @@ script:
fi fi
- | - |
if [ "${SSL}" = "YES" ] && [ "${SECURITY}" = "YES" ]; then if [ "${SSL}" = "YES" ] && [ "${SECURITY}" = "YES" ]; then
cmake --build . --config ${BUILD_TYPE} --target schema && \
cmake --build . --config ${BUILD_TYPE} --target options_doc && \
diff --strip-trailing-cr ../etc/cyclonedds.rnc docs/cyclonedds.rnc && \ diff --strip-trailing-cr ../etc/cyclonedds.rnc docs/cyclonedds.rnc && \
diff --strip-trailing-cr ../etc/cyclonedds.xsd docs/cyclonedds.xsd && \ diff --strip-trailing-cr ../etc/cyclonedds.xsd docs/cyclonedds.xsd && \
diff --strip-trailing-cr ../docs/manual/options.md docs/manual/options.md diff --strip-trailing-cr ../docs/manual/options.md docs/manual/options.md

View file

@ -210,8 +210,12 @@ option(BUILD_TESTING "Build the testing tree." OFF)
include(CTest) include(CTest)
option(BUILD_DOCS "Build documentation." OFF) option(BUILD_DOCS "Build documentation." OFF)
option(BUILD_SCHEMA "Build generated schema for configuration options." OFF) if(CMAKE_CROSSCOMPILING)
set(not_crosscompiling OFF)
else()
set(not_crosscompiling ON)
endif()
option(BUILD_SCHEMA "Build generated schema for configuration options." ${not_crosscompiling})
# Build all executables and libraries into the top-level /bin and /lib folders. # Build all executables and libraries into the top-level /bin and /lib folders.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

View file

@ -18,18 +18,14 @@ set(options_md "${CMAKE_CURRENT_BINARY_DIR}/manual/options.md")
if(BUILD_SCHEMA OR BUILD_DOCS) if(BUILD_SCHEMA OR BUILD_DOCS)
add_custom_command( add_custom_command(
OUTPUT "${cyclonedds_rnc}" "${cyclonedds_xsd}" OUTPUT "${cyclonedds_rnc}" "${cyclonedds_xsd}" "${options_md}"
COMMAND ddsconf ARGS -f rnc -o "${cyclonedds_rnc}" COMMAND ddsconf ARGS -f rnc -o "${cyclonedds_rnc}"
COMMAND ddsconf ARGS -f xsd -o "${cyclonedds_xsd}" COMMAND ddsconf ARGS -f xsd -o "${cyclonedds_xsd}"
DEPENDS ddsconf)
add_custom_target(schema DEPENDS "${cyclonedds_rnc}" "${cyclonedds_xsd}")
add_custom_command(
OUTPUT "${options_md}"
COMMAND ${CMAKE_COMMAND} -E make_directory manual COMMAND ${CMAKE_COMMAND} -E make_directory manual
COMMAND ddsconf ARGS -f md -o "${options_md}" COMMAND ddsconf ARGS -f md -o "${options_md}"
DEPENDS ddsconf) DEPENDS ddsconf)
add_custom_target(options_doc DEPENDS "${options_md}") add_custom_target(
schema ALL DEPENDS "${cyclonedds_rnc}" "${cyclonedds_xsd}" "${options_md}")
endif() endif()
if(BUILD_DOCS) if(BUILD_DOCS)
@ -39,7 +35,7 @@ if(BUILD_DOCS)
BREATHE_PROJECTS ddsc_api_docs BREATHE_PROJECTS ddsc_api_docs
BUILDER html BUILDER html
SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/manual") SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/manual")
add_dependencies(docs options_doc) add_dependencies(docs schema)
install( install(
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/docs" DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/docs"

View file

@ -1695,4 +1695,4 @@ While none prevents any message from being written to a DDSI2 log file.
The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful verbosity levels are config, fine and finest. The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful verbosity levels are config, fine and finest.
The default value is: "none". The default value is: "none".

View file

@ -101,7 +101,7 @@ CycloneDDS configuration""" ] ]
text text
} }
}* }*
}? }*
& [ a:documentation [ xml:lang="en" """ & [ a:documentation [ xml:lang="en" """
<p>This element statically configures an addresses for discovery.</p>""" ] ] <p>This element statically configures an addresses for discovery.</p>""" ] ]
element Peer { element Peer {
@ -983,7 +983,7 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==<br>
}? }?
}? }?
}? }?
}* }?
& [ a:documentation [ xml:lang="en" """ & [ a:documentation [ xml:lang="en" """
<p>The Sizing element specifies a variety of configuration settings dealing with expected system sizes, buffer sizes, &c.</p>""" ] ] <p>The Sizing element specifies a variety of configuration settings dealing with expected system sizes, buffer sizes, &c.</p>""" ] ]
element Sizing { element Sizing {
@ -1180,4 +1180,4 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==<br>
duration = xsd:token { pattern = "0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)" } duration = xsd:token { pattern = "0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)" }
duration_inf = xsd:token { pattern = "inf|0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)" } duration_inf = xsd:token { pattern = "inf|0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)" }
memsize = xsd:token { pattern = "0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B" } memsize = xsd:token { pattern = "0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B" }
} }

View file

@ -17,17 +17,17 @@ CycloneDDS configuration</xs:documentation>
&lt;p&gt;The General element specifying Domain related settings.&lt;/p&gt;</xs:documentation> &lt;p&gt;The General element specifying Domain related settings.&lt;/p&gt;</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:complexType> <xs:complexType>
<xs:choice minOccurs="0"> <xs:all>
<xs:element ref="config:Compatibility"/> <xs:element minOccurs="0" ref="config:Compatibility"/>
<xs:element ref="config:Discovery"/> <xs:element minOccurs="0" ref="config:Discovery"/>
<xs:element ref="config:General"/> <xs:element minOccurs="0" ref="config:General"/>
<xs:element ref="config:Internal"/> <xs:element minOccurs="0" ref="config:Internal"/>
<xs:element ref="config:Partitioning"/> <xs:element minOccurs="0" ref="config:Partitioning"/>
<xs:element ref="config:SSL"/> <xs:element minOccurs="0" ref="config:SSL"/>
<xs:element maxOccurs="unbounded" ref="config:Security"/> <xs:element minOccurs="0" ref="config:Security"/>
<xs:element ref="config:Sizing"/> <xs:element minOccurs="0" ref="config:Sizing"/>
<xs:element ref="config:TCP"/> <xs:element minOccurs="0" ref="config:TCP"/>
<xs:element ref="config:ThreadPool"/> <xs:element minOccurs="0" ref="config:ThreadPool"/>
<xs:element minOccurs="0" name="Threads"> <xs:element minOccurs="0" name="Threads">
<xs:annotation> <xs:annotation>
<xs:documentation> <xs:documentation>
@ -39,8 +39,8 @@ CycloneDDS configuration</xs:documentation>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element ref="config:Tracing"/> <xs:element minOccurs="0" ref="config:Tracing"/>
</xs:choice> </xs:all>
<xs:attribute name="Id"> <xs:attribute name="Id">
<xs:annotation> <xs:annotation>
<xs:documentation> <xs:documentation>
@ -179,9 +179,9 @@ CycloneDDS configuration</xs:documentation>
&lt;p&gt;This element statically configures addresses for discovery.&lt;/p&gt;</xs:documentation> &lt;p&gt;This element statically configures addresses for discovery.&lt;/p&gt;</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:complexType> <xs:complexType>
<xs:choice minOccurs="0"> <xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="config:Group"/> <xs:element ref="config:Group"/>
<xs:element maxOccurs="unbounded" ref="config:Peer"/> <xs:element ref="config:Peer"/>
</xs:choice> </xs:choice>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
@ -1779,4 +1779,4 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==&lt;br&gt;
<xs:pattern value="0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B"/> <xs:pattern value="0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>
</xs:schema> </xs:schema>

View file

@ -1626,7 +1626,9 @@ static struct cfgelem discovery_peers_cfgelems[] = {
"<p>This element statically configures a fault tolerant group of " "<p>This element statically configures a fault tolerant group of "
"addresses for discovery. Each member of the group is tried in " "addresses for discovery. Each member of the group is tried in "
"sequence until one succeeds.</p>" "sequence until one succeeds.</p>"
)), ),
MAXIMUM(0)), /* Group element can occur more than once, but 1 is required
because of the way its processed (for now) */
END_MARKER END_MARKER
}; };
@ -1849,7 +1851,9 @@ static struct cfgelem domain_cfgelems[] = {
DESCRIPTION( DESCRIPTION(
"<p>This element is used to configure Cyclone DDS with the DDS Security " "<p>This element is used to configure Cyclone DDS with the DDS Security "
"specification plugins and settings.</p>" "specification plugins and settings.</p>"
)), ),
MAXIMUM(1)), /* Security must occur at most once, but INT_MAX is required
because of the way its processed (for now) */
#endif #endif
#ifdef DDSI_INCLUDE_NETWORK_PARTITIONS #ifdef DDSI_INCLUDE_NETWORK_PARTITIONS
GROUP("Partitioning", partitioning_cfgelems, NULL, 1, GROUP("Partitioning", partitioning_cfgelems, NULL, 1,

View file

@ -230,6 +230,8 @@ DI(if_omg_security);
#define RANGE(...) /* drop */ #define RANGE(...) /* drop */
#define UNIT(...) /* drop */ #define UNIT(...) /* drop */
#define VALUES(...) /* drop */ #define VALUES(...) /* drop */
#define MAXIMUM(...) /* drop */
#define MINIMUM(...) /* drop */
#define NOMEMBER 0, 0 #define NOMEMBER 0, 0
#define NOFUNCTIONS 0, 0, 0, 0 #define NOFUNCTIONS 0, 0, 0, 0
@ -269,6 +271,12 @@ static const struct cfgelem root_cfgelem = {
#undef MEMBER #undef MEMBER
#undef MEMBEROF #undef MEMBEROF
#undef FUNCTIONS #undef FUNCTIONS
#undef DESCRIPTION
#undef RANGE
#undef UNIT
#undef VALUES
#undef MAXIMUM
#undef MINIMUM
#undef NOMEMBER #undef NOMEMBER
#undef NOFUNCTIONS #undef NOFUNCTIONS
#undef NODATA #undef NODATA

View file

@ -50,10 +50,12 @@
#define RANGE(str) .range = str #define RANGE(str) .range = str
#define UNIT(str) .unit = str #define UNIT(str) .unit = str
#define VALUES(...) .values = (const char *[]){ __VA_ARGS__, NULL } #define VALUES(...) .values = (const char *[]){ __VA_ARGS__, NULL }
#define MAXIMUM(num) .force_maximum = 1, .maximum = num
#define MINIMUM(num) .force_minimum = 1, .minimum = num
#define NOMEMBER /* drop */ #define NOMEMBER /* drop */
#define NOFUNCTIONS /* drop */ #define NOFUNCTIONS /* drop */
#define NOMETADATA { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL } #define NOMETADATA { NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
#define END_MARKER { NULL, NULL, NULL, 0, NULL, NULL, NOMETADATA } #define END_MARKER { NULL, NULL, NULL, 0, NULL, NULL, NOMETADATA }
#define ELEMENT(name, elems, attrs, multip, dflt, desc, ...) \ #define ELEMENT(name, elems, attrs, multip, dflt, desc, ...) \
@ -76,8 +78,8 @@
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "string", __VA_ARGS__)) EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "string", __VA_ARGS__))
#define LIST(name, attrs, multip, dflt, ofst, funcs, desc, ...) \ #define LIST(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "list", __VA_ARGS__)) EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "list", __VA_ARGS__))
#define GROUP(name, elems, attrs, multip, ofst, funcs, desc) \ #define GROUP(name, elems, attrs, multip, ofst, funcs, desc, ...) \
EXPAND(ELEMENT, (name, elems, attrs, multip, NULL, desc, .type = "group")) EXPAND(ELEMENT, (name, elems, attrs, multip, NULL, desc, .type = "group", __VA_ARGS__))
#include "dds/ddsi/ddsi_cfgelems.h" #include "dds/ddsi/ddsi_cfgelems.h"
/* undefine element macros */ /* undefine element macros */
@ -89,6 +91,8 @@
#undef RANGE #undef RANGE
#undef UNIT #undef UNIT
#undef VALUES #undef VALUES
#undef MAXIMUM
#undef MINIMUM
#undef NOMEMBER #undef NOMEMBER
#undef NOFUNCTIONS #undef NOFUNCTIONS
#undef NOMETADATA #undef NOMETADATA
@ -228,27 +232,35 @@ int islist(const struct cfgelem *elem)
int minimum(const struct cfgelem *elem) int minimum(const struct cfgelem *elem)
{ {
switch (elem->multiplicity) { if (elem->meta.force_minimum) {
case 0: /* special case, treat as-if 1 */ return elem->meta.minimum;
case 1: /* required if there is no default */ } else {
if (isgroup(elem)) switch (elem->multiplicity) {
case 0: /* special case, treat as-if 1 */
case 1: /* required if there is no default */
if (isgroup(elem))
return 0;
return (!elem->value);
default:
return 0; return 0;
return (!elem->value); }
default:
return 0;
} }
} }
int maximum(const struct cfgelem *elem) int maximum(const struct cfgelem *elem)
{ {
switch (elem->multiplicity) { if (elem->meta.force_maximum) {
case INT_MAX: return elem->meta.maximum;
return 0; } else {
case 0: switch (elem->multiplicity) {
case 1: case INT_MAX:
return 1; return 0;
default: case 0:
return elem->multiplicity; case 1:
return 1;
default:
return elem->multiplicity;
}
} }
} }

View file

@ -31,6 +31,8 @@ struct cfgmeta {
char *pattern; char *pattern;
char *description; char *description;
unsigned int flags; unsigned int flags;
const int force_maximum, maximum;
const int force_minimum, minimum;
const char *type; const char *type;
const char *unit; const char *unit;
const char *range; const char *range;

View file

@ -103,32 +103,32 @@ static void printtype(
(void)units; (void)units;
assert(!isgroup(elem)); assert(!isgroup(elem));
if (isbool(elem)) { if (isbool(elem)) {
fprintf(out, "Boolean\n"); fputs("Boolean\n", out);
} else if (islist(elem)) { } else if (islist(elem)) {
assert(elem->meta.values); assert(elem->meta.values);
fprintf(out, "One of:\n"); fputs("One of:\n", out);
if (elem->value && strlen(elem->value)) if (elem->value && strlen(elem->value))
fprintf(out, "* Keyword: %s\n", elem->value); fprintf(out, "* Keyword: %s\n", elem->value);
fprintf(out, "* Comma-separated list of: "); fputs("* Comma-separated list of: ", out);
for (const char **v = elem->meta.values; *v; v++) { for (const char **v = elem->meta.values; *v; v++) {
fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v); fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v);
} }
fprintf(out, "\n"); fputs("\n", out);
if (!elem->value || !strlen(elem->value)) if (!elem->value || !strlen(elem->value))
fprintf(out, "* Or empty\n"); fputs("* Or empty\n", out);
} else if (isenum(elem)) { } else if (isenum(elem)) {
assert(elem->meta.values); assert(elem->meta.values);
fprintf(out, "One of: "); fputs("One of: ", out);
for (const char **v = elem->meta.values; *v; v++) { for (const char **v = elem->meta.values; *v; v++) {
fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v); fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v);
} }
fprintf(out, "\n"); fputs("\n", out);
} else if (isint(elem)) { } else if (isint(elem)) {
fprintf(out, "Integer\n"); fputs("Integer\n", out);
} else if (elem->meta.unit) { } else if (elem->meta.unit) {
fprintf(out, "Number-with-unit\n"); fputs("Number-with-unit\n", out);
} else if (isstring(elem)) { } else if (isstring(elem)) {
fprintf(out, "Text\n"); fputs("Text\n", out);
} }
} }
@ -142,12 +142,14 @@ static void printattr(
const struct cfgunit *units) const struct cfgunit *units)
{ {
if (flags & FLAG_LF) if (flags & FLAG_LF)
fputs("\n\n\n", out); fputs("\n\n", out);
printhead(out, level, flags, elem, units); printhead(out, level, flags, elem, units);
printtype(out, level, flags, elem, units); printtype(out, level, flags, elem, units);
fputs("\n", out); fputs("\n", out);
if (elem->description) if (elem->description) {
fputs(elem->meta.description, out); fputs(elem->meta.description, out);
fputs("\n", out);
}
} }
static void printelem( static void printelem(
@ -158,7 +160,7 @@ static void printelem(
const struct cfgunit *units) const struct cfgunit *units)
{ {
if (flags & FLAG_LF) if (flags & FLAG_LF)
fprintf(out, "\n\n\n"); fputs("\n\n", out);
printhead(out, level, flags, elem, units); printhead(out, level, flags, elem, units);
flags &= ~FLAG_LF; flags &= ~FLAG_LF;
if (hasattributes(elem)) { if (hasattributes(elem)) {
@ -176,7 +178,7 @@ static void printelem(
ce = nextelem(elem->attributes, ce); ce = nextelem(elem->attributes, ce);
} }
if (cnt != 0) { if (cnt != 0) {
fprintf(out, "\n"); fputs("\n", out);
flags |= FLAG_LF; flags |= FLAG_LF;
} }
} }
@ -190,18 +192,19 @@ static void printelem(
sep = ", "; sep = ", ";
ce = nextelem(elem->children, ce); ce = nextelem(elem->children, ce);
} }
fprintf(out, "\n"); fputs("\n", out);
flags |= FLAG_LF; flags |= FLAG_LF;
} else if (!isgroup(elem)) { } else if (!isgroup(elem)) {
if (flags & FLAG_LF) if (flags & FLAG_LF)
fprintf(out, "\n"); fputs("\n", out);
printtype(out, level+1, flags, elem, units); printtype(out, level+1, flags, elem, units);
flags |= FLAG_LF; flags |= FLAG_LF;
} }
if (elem->description) { if (elem->description) {
if (flags & FLAG_LF) if (flags & FLAG_LF)
fprintf(out, "\n"); fputs("\n", out);
fputs(elem->meta.description, out); fputs(elem->meta.description, out);
fputs("\n", out);
} }
if (hasattributes(elem)) { if (hasattributes(elem)) {
struct cfgelem *ce = firstelem(elem->attributes); struct cfgelem *ce = firstelem(elem->attributes);

View file

@ -158,6 +158,6 @@ int printrnc(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
static const char *fmt = " %s = xsd:token { pattern = \"%s\" }\n"; static const char *fmt = " %s = xsd:token { pattern = \"%s\" }\n";
print(out, 0, fmt, cu->name, cu->pattern); print(out, 0, fmt, cu->name, cu->pattern);
} }
print(out, 0, "}"); print(out, 0, "}\n");
return 0; return 0;
} }

View file

@ -165,7 +165,7 @@ printref(
snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem)); snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem));
if (!(flags & FLAG_NOMAX) && maximum(elem) == 0) if (!(flags & FLAG_NOMAX) && maximum(elem) == 0)
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" "); snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" ");
else if (!(FLAG_NOMAX) && maximum(elem) != 1) else if (!(flags & FLAG_NOMAX) && maximum(elem) != 1)
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem)); snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem));
print(out, cols, fmt, minattr, maxattr, schema(), name(elem)); print(out, cols, fmt, minattr, maxattr, schema(), name(elem));
} }
@ -185,11 +185,11 @@ printcomplextype(
assert(!ismoved(elem) && !isdeprecated(elem)); assert(!ismoved(elem) && !isdeprecated(elem));
if (flags & FLAG_REFERENCE) { if (flags & FLAG_REFERENCE) {
if (minimum(elem) != 1) if (!(flags & FLAG_NOMIN) && minimum(elem) != 1)
snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem)); snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem));
if (maximum(elem) == 0) if (!(flags & FLAG_NOMAX) && maximum(elem) == 0)
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" "); snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" ");
else if (maximum(elem) != 1) else if (!(flags & FLAG_NOMAX) && maximum(elem) != 1)
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem)); snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem));
} }
@ -208,58 +208,63 @@ printcomplextype(
if ((cnt = haschildren(elem))) { if ((cnt = haschildren(elem))) {
const char *cont = NULL; const char *cont = NULL;
struct cfgelem *ce; struct cfgelem *ce;
int min[2], max[2], eq[2] = { 1, 1 }; int min[3], max[3];
int mineq, maxeq;
assert(isgroup(elem)); assert(isgroup(elem));
minattr[0] = '\0'; minattr[0] = '\0';
maxattr[0] = '\0'; maxattr[0] = '\0';
if (cnt == 1) { if (cnt == 1) {
/* minOccurs and maxOccurs placed in element */
cont = "sequence"; cont = "sequence";
} else { } else {
assert(cnt > 1); assert(cnt > 1);
ce = firstelem(elem->children); ce = firstelem(elem->children);
assert(ce); min[0] = min[1] = min[2] = minimum(ce);
min[0] = min[1] = minimum(ce); max[0] = max[1] = max[2] = maximum(ce);
max[0] = max[1] = maximum(ce);
assert(min[1] <= max[1] || max[1] == 0); assert(min[1] <= max[1] || max[1] == 0);
mineq = maxeq = 1;
ce = nextelem(elem->children, ce); ce = nextelem(elem->children, ce);
assert(ce); assert(ce);
while (ce) { while (ce) {
min[1] = minimum(ce); min[1] = minimum(ce);
max[1] = maximum(ce); max[1] = maximum(ce);
assert(min[1] <= max[1] || max[1] == 0); assert(min[1] <= max[1] || max[1] == 0);
min[2] += minimum(ce);
max[2] += maximum(ce);
if (min[1] != min[0]) { if (min[1] != min[0]) {
eq[0] = 0; mineq = 0;
if (min[1] < min[0]) if (min[1] > min[0])
min[0] = min[1]; min[0] = min[1];
} }
if (max[1] != max[0]) { if (max[1] != max[0]) {
eq[1] = 0; maxeq = 0;
if ((max[0] != 0 && max[1] > max[0]) || max[1] == 0) if ((max[0] != 0 && max[1] > max[0]) || max[1] == 0)
max[0] = max[1]; max[0] = max[1];
} }
ce = nextelem(elem->children, ce); ce = nextelem(elem->children, ce);
} }
if (min[0] > 1 || max[0] != 1 /* unbounded or >1 */) { /* xsd generation becomes significantly more difficult if the minimum
cont = "choice"; number of occurences for an element is more non-zero and the
if (eq[0]) { maximum number of occurences of it (or one of its siblings) is more
if (min[0] != 1) than one, but that is not likely to occur */
snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[0]); if (min[0] <= 1 && max[0] == 1) {
flags |= FLAG_NOMIN;
}
if (eq[1]) {
if (max[0] == 0)
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\"");
else if (max[0] != 1)
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[0]);
flags |= FLAG_NOMAX;
}
} else {
/* any order, each zero or one time */ /* any order, each zero or one time */
/* minOccurs placed in element maxOccurs always one */
cont = "all"; cont = "all";
} else {
cont = "choice";
if (min[0] == 0)
snprintf(minattr, sizeof(minattr), " minOccurs=\"0\"");
else if (min[0] != 1) /* incorrect, but make the most of it */
snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[2]);
if (max[0] == 0)
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\"");
else if (max[0] != 1)
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[2]);
if (mineq)
flags |= FLAG_NOMIN;
if (maxeq)
flags |= FLAG_NOMAX;
} }
} }
@ -270,7 +275,6 @@ printcomplextype(
ce = nextelem(elem->children, ce); ce = nextelem(elem->children, ce);
} }
print(out, cols+4, "</xs:%s>\n", cont); print(out, cols+4, "</xs:%s>\n", cont);
flags &= ~(FLAG_NOMIN | FLAG_NOMAX);
} else if (!isgroup(elem) && (!isstring(elem) || elem->meta.unit)) { } else if (!isgroup(elem) && (!isstring(elem) || elem->meta.unit)) {
ofst = 4; ofst = 4;
print(out, cols+4, "<xs:simpleContent>\n"); print(out, cols+4, "<xs:simpleContent>\n");
@ -288,6 +292,7 @@ printcomplextype(
print(out, cols+6, extfmt, "xs", isbuiltintype(elem)); print(out, cols+6, extfmt, "xs", isbuiltintype(elem));
} }
} }
flags &= ~(FLAG_NOMIN | FLAG_NOMAX);
if (hasattributes(elem)) { if (hasattributes(elem)) {
struct cfgelem *ce; struct cfgelem *ce;
ce = firstelem(elem->attributes); ce = firstelem(elem->attributes);
@ -417,6 +422,6 @@ int printxsd(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
print(out, 4, "</xs:restriction>\n"); print(out, 4, "</xs:restriction>\n");
print(out, 2, "</xs:simpleType>\n"); print(out, 2, "</xs:simpleType>\n");
} }
print(out, 0, "</xs:schema>"); print(out, 0, "</xs:schema>\n");
return 0; return 0;
} }