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

@ -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 {

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>

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,6 +232,9 @@ int islist(const struct cfgelem *elem)
int minimum(const struct cfgelem *elem) int minimum(const struct cfgelem *elem)
{ {
if (elem->meta.force_minimum) {
return elem->meta.minimum;
} else {
switch (elem->multiplicity) { switch (elem->multiplicity) {
case 0: /* special case, treat as-if 1 */ case 0: /* special case, treat as-if 1 */
case 1: /* required if there is no default */ case 1: /* required if there is no default */
@ -237,10 +244,14 @@ int minimum(const struct cfgelem *elem)
default: default:
return 0; return 0;
} }
}
} }
int maximum(const struct cfgelem *elem) int maximum(const struct cfgelem *elem)
{ {
if (elem->meta.force_maximum) {
return elem->meta.maximum;
} else {
switch (elem->multiplicity) { switch (elem->multiplicity) {
case INT_MAX: case INT_MAX:
return 0; return 0;
@ -250,6 +261,7 @@ int maximum(const struct cfgelem *elem)
default: default:
return elem->multiplicity; return elem->multiplicity;
} }
}
} }
int haschildren(const struct cfgelem *elem) int haschildren(const struct cfgelem *elem)

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,59 +208,64 @@ 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
number of occurences for an element is more non-zero and the
maximum number of occurences of it (or one of its siblings) is more
than one, but that is not likely to occur */
if (min[0] <= 1 && max[0] == 1) {
/* any order, each zero or one time */
cont = "all";
} else {
cont = "choice"; cont = "choice";
if (eq[0]) { if (min[0] == 0)
if (min[0] != 1) snprintf(minattr, sizeof(minattr), " minOccurs=\"0\"");
snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[0]); else if (min[0] != 1) /* incorrect, but make the most of it */
flags |= FLAG_NOMIN; snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[2]);
}
if (eq[1]) {
if (max[0] == 0) if (max[0] == 0)
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\""); snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\"");
else if (max[0] != 1) else if (max[0] != 1)
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[0]); snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[2]);
if (mineq)
flags |= FLAG_NOMIN;
if (maxeq)
flags |= FLAG_NOMAX; flags |= FLAG_NOMAX;
} }
} else {
/* any order, each zero or one time */
/* minOccurs placed in element maxOccurs always one */
cont = "all";
}
} }
print(out, cols+4, "<xs:%s%s%s>\n", cont, minattr, maxattr); print(out, cols+4, "<xs:%s%s%s>\n", cont, minattr, maxattr);
@ -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;
} }