835 lines
26 KiB
C
835 lines
26 KiB
C
/*
|
|
* XDR (XML-Data Reduced) -> XSD (XML Schema Document) conversion
|
|
*
|
|
* Copyright 2010 Adam Martinson for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#ifdef HAVE_LIBXML2
|
|
# include <libxml/tree.h>
|
|
#endif
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msxml);
|
|
|
|
/* Both XDR and XSD are valid XML
|
|
* We just convert the doc tree, no need for a parser.
|
|
*/
|
|
|
|
#ifdef HAVE_LIBXML2
|
|
|
|
static const xmlChar DT_prefix[] = "dt";
|
|
static const xmlChar DT_href[] = "urn:schemas-microsoft-com:datatypes";
|
|
static const xmlChar XDR_prefix[] = "xdr";
|
|
static const xmlChar XDR_href[] = "urn:schemas-microsoft-com:xml-data";
|
|
static const xmlChar XSD_prefix[] = "xsd";
|
|
static const xmlChar XSD_href[] = "http://www.w3.org/2001/XMLSchema";
|
|
|
|
static const xmlChar xs_all[] = "all";
|
|
static const xmlChar xs_annotation[] = "annotation";
|
|
static const xmlChar xs_any[] = "any";
|
|
static const xmlChar xs_anyAttribute[] = "anyAttribute";
|
|
static const xmlChar xs_attribute[] = "attribute";
|
|
static const xmlChar xs_AttributeType[] = "AttributeType";
|
|
static const xmlChar xs_base[] = "base";
|
|
static const xmlChar xs_choice[] = "choice";
|
|
static const xmlChar xs_complexContent[] = "complexContent";
|
|
static const xmlChar xs_complexType[] = "complexType";
|
|
static const xmlChar xs_content[] = "content";
|
|
static const xmlChar xs_datatype[] = "datatype";
|
|
static const xmlChar xs_default[] = "default";
|
|
static const xmlChar xs_description[] = "description";
|
|
static const xmlChar xs_documentation[] = "documentation";
|
|
static const xmlChar xs_element[] = "element";
|
|
static const xmlChar xs_ElementType[] = "ElementType";
|
|
static const xmlChar xs_eltOnly[] = "eltOnly";
|
|
static const xmlChar xs_empty[] = "empty";
|
|
static const xmlChar xs_enumeration[] = "enumeration";
|
|
static const xmlChar xs_extension[] = "extension";
|
|
static const xmlChar xs_group[] = "group";
|
|
static const xmlChar xs_lax[] = "lax";
|
|
static const xmlChar xs_length[] = "length";
|
|
static const xmlChar xs_many[] = "many";
|
|
static const xmlChar xs_maxOccurs[] = "maxOccurs";
|
|
static const xmlChar xs_minOccurs[] = "minOccurs";
|
|
static const xmlChar xs_mixed[] = "mixed";
|
|
static const xmlChar xs_model[] = "model";
|
|
static const xmlChar xs_name[] = "name";
|
|
static const xmlChar xs_namespace[] = "namespace";
|
|
static const xmlChar xs_no[] = "no";
|
|
static const xmlChar xs_one[] = "one";
|
|
static const xmlChar xs_open[] = "open";
|
|
static const xmlChar xs_optional[] = "optional";
|
|
static const xmlChar xs_order[] = "order";
|
|
static const xmlChar xs_processContents[] = "processContents";
|
|
static const xmlChar xs_ref[] = "ref";
|
|
static const xmlChar xs_required[] = "required";
|
|
static const xmlChar xs_restriction[] = "restriction";
|
|
static const xmlChar xs_schema[] = "schema";
|
|
static const xmlChar xs_Schema[] = "Schema";
|
|
static const xmlChar xs_seq[] = "seq";
|
|
static const xmlChar xs_sequence[] = "sequence";
|
|
static const xmlChar xs_simpleContent[] = "simpleContent";
|
|
static const xmlChar xs_simpleType[] = "simpleType";
|
|
static const xmlChar xs_strict[] = "strict";
|
|
static const xmlChar xs_targetNamespace[] = "targetNamespace";
|
|
static const xmlChar xs_textOnly[] = "textOnly";
|
|
static const xmlChar xs_true[] = "true";
|
|
static const xmlChar xs_type[] = "type";
|
|
static const xmlChar xs_unbounded[] = "unbounded";
|
|
static const xmlChar xs_use[] = "use";
|
|
static const xmlChar xs_value[] = "value";
|
|
static const xmlChar xs_values[] = "values";
|
|
static const xmlChar xs_xsd_string[] = "xsd:string";
|
|
static const xmlChar xs_yes[] = "yes";
|
|
|
|
typedef enum _CONTENT_TYPE
|
|
{
|
|
CONTENT_EMPTY,
|
|
CONTENT_TEXTONLY,
|
|
CONTENT_ELTONLY,
|
|
CONTENT_MIXED
|
|
} CONTENT_TYPE;
|
|
|
|
typedef enum _ORDER_TYPE
|
|
{
|
|
ORDER_SEQ,
|
|
ORDER_MANY,
|
|
ORDER_ONE
|
|
} ORDER_TYPE;
|
|
|
|
#define FOREACH_CHILD(node, child) \
|
|
for (child = node->children; child != NULL; child = child->next) \
|
|
if (child->type == XML_ELEMENT_NODE)
|
|
|
|
#define FOREACH_ATTR(node, attr) \
|
|
for (attr = node->properties; attr != NULL; attr = attr->next)
|
|
|
|
#define FOREACH_NS(node, ns) \
|
|
for (ns = node->nsDef; ns != NULL; ns = ns->next)
|
|
|
|
static inline xmlNodePtr get_schema(xmlNodePtr node)
|
|
{
|
|
return xmlDocGetRootElement(node->doc);
|
|
}
|
|
|
|
static inline xmlNodePtr get_child(xmlNodePtr node, xmlChar const* name)
|
|
{
|
|
xmlNodePtr child = NULL;
|
|
if (node)
|
|
{
|
|
FOREACH_CHILD(node, child)
|
|
{
|
|
if (xmlStrEqual(child->name, name))
|
|
break;
|
|
}
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
static inline xmlNodePtr get_child_with_attr(xmlNodePtr node, xmlChar const* name,
|
|
xmlChar const* attr_ns, xmlChar const* attr_name,
|
|
xmlChar const* attr_val)
|
|
{
|
|
xmlChar* str;
|
|
if (node)
|
|
{
|
|
FOREACH_CHILD(node, node)
|
|
{
|
|
if (xmlStrEqual(node->name, name))
|
|
{
|
|
str = (attr_ns != NULL)? xmlGetNsProp(node, attr_name, attr_ns) :
|
|
xmlGetProp(node, attr_name);
|
|
if (str)
|
|
{
|
|
if (xmlStrEqual(str, attr_val))
|
|
{
|
|
xmlFree(str);
|
|
return node;
|
|
}
|
|
xmlFree(str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static inline xmlNsPtr get_dt_ns(xmlNodePtr node)
|
|
{
|
|
xmlNsPtr ns;
|
|
|
|
node = get_schema(node);
|
|
assert(node != NULL);
|
|
|
|
FOREACH_NS(node, ns)
|
|
{
|
|
if (xmlStrEqual(ns->href, DT_href))
|
|
break;
|
|
}
|
|
|
|
return ns;
|
|
}
|
|
|
|
static inline xmlChar* get_dt_type(xmlNodePtr xdr)
|
|
{
|
|
xmlChar* str = xmlGetNsProp(xdr, xs_type, DT_href);
|
|
if (!str)
|
|
{
|
|
xmlNodePtr datatype = get_child(xdr, xs_datatype);
|
|
if (datatype)
|
|
str = xmlGetNsProp(datatype, xs_type, DT_href);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
static inline xmlChar* get_attr_val(xmlAttrPtr attr)
|
|
{
|
|
return xmlNodeGetContent((xmlNodePtr)attr);
|
|
}
|
|
|
|
static inline xmlNodePtr add_any_child(xmlNodePtr parent, BOOL set_occurs)
|
|
{
|
|
xmlNodePtr child = xmlNewChild(parent, NULL, xs_any, NULL);
|
|
if (set_occurs)
|
|
{
|
|
xmlSetProp(child, xs_minOccurs, BAD_CAST "0");
|
|
xmlSetProp(child, xs_maxOccurs, xs_unbounded);
|
|
}
|
|
xmlSetProp(child, xs_processContents, xs_strict);
|
|
return child;
|
|
}
|
|
|
|
static inline xmlNodePtr add_anyAttribute_child(xmlNodePtr parent)
|
|
{
|
|
xmlNodePtr child = xmlNewChild(parent, NULL, xs_anyAttribute, NULL);
|
|
xmlSetProp(child, xs_processContents, xs_lax);
|
|
return child;
|
|
}
|
|
|
|
static inline xmlAttrPtr copy_prop_ignore_ns(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
xmlChar* str = get_attr_val(xdr_attr);
|
|
xmlAttrPtr attr = xmlSetProp(node, xdr_attr->name, str);
|
|
xmlFree(str);
|
|
return attr;
|
|
}
|
|
static inline xmlAttrPtr XDR_A_default(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
TRACE("(%p, %p)\n", xdr_attr, node);
|
|
|
|
return copy_prop_ignore_ns(xdr_attr, node);
|
|
}
|
|
|
|
static inline xmlAttrPtr XDR_A_dt_type(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
xmlChar* str = get_attr_val(xdr_attr);
|
|
xmlAttrPtr attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr_attr, node);
|
|
|
|
if (xmlStrEqual(str, xs_enumeration))
|
|
attr = NULL;
|
|
else
|
|
attr = xmlSetNsProp(node, get_dt_ns(node), DT_prefix, str);
|
|
xmlFree(str);
|
|
return attr;
|
|
}
|
|
|
|
static xmlAttrPtr XDR_A_maxOccurs(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
xmlChar* str = get_attr_val(xdr_attr);
|
|
xmlAttrPtr attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr_attr, node);
|
|
|
|
if (xmlStrEqual(str, BAD_CAST "*"))
|
|
attr = xmlSetProp(node, xs_maxOccurs, xs_unbounded);
|
|
else
|
|
attr = copy_prop_ignore_ns(xdr_attr, node);
|
|
|
|
xmlFree(str);
|
|
return attr;
|
|
}
|
|
|
|
static inline xmlAttrPtr XDR_A_minOccurs(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
TRACE("(%p, %p)\n", xdr_attr, node);
|
|
|
|
return copy_prop_ignore_ns(xdr_attr, node);
|
|
}
|
|
|
|
static inline xmlAttrPtr XDR_A_name(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
TRACE("(%p, %p)\n", xdr_attr, node);
|
|
|
|
return copy_prop_ignore_ns(xdr_attr, node);
|
|
}
|
|
|
|
static xmlAttrPtr XDR_A_type(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
xmlChar* str = get_attr_val(xdr_attr);
|
|
xmlAttrPtr attr = xmlSetProp(node, xs_ref, str);
|
|
|
|
TRACE("(%p, %p)\n", xdr_attr, node);
|
|
|
|
xmlFree(str);
|
|
return attr;
|
|
}
|
|
|
|
static xmlAttrPtr XDR_A_required(xmlAttrPtr xdr_attr, xmlNodePtr node)
|
|
{
|
|
xmlChar* str = get_attr_val(xdr_attr);
|
|
xmlAttrPtr attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr_attr, node);
|
|
|
|
if (xmlStrEqual(str, xs_no))
|
|
attr = xmlSetProp(node, xs_use, xs_optional);
|
|
else /* yes */
|
|
attr = xmlSetProp(node, xs_use, xs_required);
|
|
xmlFree(str);
|
|
return attr;
|
|
}
|
|
|
|
static xmlNodePtr XDR_E_description(xmlNodePtr xdr, xmlNodePtr parent)
|
|
{
|
|
xmlNodePtr xsd_node = xmlNewChild(parent, NULL, xs_annotation, NULL);
|
|
xmlAttrPtr xdr_attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr, parent);
|
|
|
|
xmlNewChild(xsd_node, NULL, xs_documentation, xdr->content);
|
|
|
|
FOREACH_ATTR(xdr, xdr_attr)
|
|
{
|
|
xmlCopyProp(xsd_node, xdr_attr);
|
|
}
|
|
return xsd_node;
|
|
}
|
|
|
|
static xmlNodePtr XDR_E_AttributeType(xmlNodePtr xdr, xmlNodePtr parent)
|
|
{
|
|
xmlChar *str, *type = get_dt_type(xdr);
|
|
xmlNodePtr xsd_node, xsd_child, xdr_child;
|
|
xmlAttrPtr xdr_attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr, parent);
|
|
|
|
xsd_node = xmlNewChild(parent, NULL, xs_attribute, NULL);
|
|
|
|
if (type && xmlStrEqual(type, xs_enumeration))
|
|
{
|
|
xmlChar *tmp, *tokBegin, *tokEnd = NULL;
|
|
xmlNodePtr xsd_enum;
|
|
xsd_child = xmlNewChild(xsd_node, NULL, xs_simpleType, NULL);
|
|
xsd_child = xmlNewChild(xsd_child, NULL, xs_restriction, NULL);
|
|
xmlSetProp(xsd_child, xs_base, xs_xsd_string);
|
|
|
|
tokBegin = str = xmlGetNsProp(xdr, xs_values, DT_href);
|
|
while (tokBegin && *tokBegin)
|
|
{
|
|
while (*tokBegin && isspace(*tokBegin))
|
|
++tokBegin;
|
|
tokEnd = tokBegin;
|
|
while (*tokEnd && !isspace(*tokEnd))
|
|
++tokEnd;
|
|
if (tokEnd == tokBegin)
|
|
break;
|
|
xsd_enum = xmlNewChild(xsd_child, NULL, xs_enumeration, NULL);
|
|
tmp = xmlStrndup(tokBegin, tokEnd-tokBegin);
|
|
xmlSetProp(xsd_enum, xs_value, tmp);
|
|
xmlFree(tmp);
|
|
tokBegin = tokEnd;
|
|
}
|
|
xmlFree(str);
|
|
|
|
}
|
|
else if (type)
|
|
{
|
|
str = xmlStrdup(DT_prefix);
|
|
str = xmlStrcat(str, BAD_CAST ":");
|
|
str = xmlStrcat(str, type);
|
|
xmlSetProp(xsd_node, xs_type, str);
|
|
xmlFree(str);
|
|
}
|
|
xmlFree(type);
|
|
|
|
FOREACH_ATTR(xdr, xdr_attr)
|
|
{
|
|
if (xmlStrEqual(xdr_attr->name, xs_default))
|
|
XDR_A_default(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_name))
|
|
XDR_A_name(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_type) && xdr_attr->ns == get_dt_ns(xdr))
|
|
XDR_A_dt_type(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_values) && xdr_attr->ns == get_dt_ns(xdr))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_attr->name, xs_required))
|
|
XDR_A_required(xdr_attr, xsd_node);
|
|
else
|
|
xmlCopyProp(xsd_node, xdr_attr);
|
|
}
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_datatype))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_child->name, xs_description))
|
|
XDR_E_description(xdr_child, xsd_node);
|
|
else
|
|
FIXME("unexpected child <%s>\n", xdr_child->name);
|
|
}
|
|
|
|
return xsd_node;
|
|
}
|
|
|
|
static xmlNodePtr XDR_E_attribute(xmlNodePtr xdr, xmlNodePtr parent)
|
|
{
|
|
xmlChar* str = xmlGetProp(xdr, xs_type);
|
|
xmlNodePtr xsd_node, xdr_child, xdr_attrType;
|
|
xmlAttrPtr xdr_attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr, parent);
|
|
|
|
xdr_attrType = get_child_with_attr(xdr->parent, xs_AttributeType, NULL, xs_name, str);
|
|
xmlFree(str);
|
|
|
|
if (xdr_attrType)
|
|
xsd_node = XDR_E_AttributeType(xdr_attrType, parent);
|
|
else
|
|
xsd_node = xmlNewChild(parent, NULL, xs_attribute, NULL);
|
|
|
|
FOREACH_ATTR(xdr, xdr_attr)
|
|
{
|
|
if (xmlStrEqual(xdr_attr->name, xs_default))
|
|
XDR_A_default(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_type) && !xdr_attrType)
|
|
XDR_A_type(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_required))
|
|
XDR_A_required(xdr_attr, xsd_node);
|
|
else
|
|
xmlCopyProp(xsd_node, xdr_attr);
|
|
}
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
FIXME("unexpected child <%s>\n", xdr_child->name);
|
|
}
|
|
|
|
return xsd_node;
|
|
}
|
|
|
|
static xmlNodePtr XDR_E_element(xmlNodePtr xdr, xmlNodePtr parent)
|
|
{
|
|
xmlNodePtr xdr_child, xsd_node = xmlNewChild(parent, NULL, xs_element, NULL);
|
|
xmlAttrPtr xdr_attr;
|
|
|
|
FOREACH_ATTR(xdr, xdr_attr)
|
|
{
|
|
if (xmlStrEqual(xdr_attr->name, xs_type))
|
|
XDR_A_type(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_maxOccurs))
|
|
XDR_A_maxOccurs(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_minOccurs))
|
|
XDR_A_minOccurs(xdr_attr, xsd_node);
|
|
else
|
|
xmlCopyProp(xsd_node, xdr_attr);
|
|
}
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
FIXME("unexpected child <%s>\n", xdr_child->name);
|
|
}
|
|
|
|
return xsd_node;
|
|
}
|
|
|
|
static xmlNodePtr XDR_E_group(xmlNodePtr xdr, xmlNodePtr parent)
|
|
{
|
|
xmlNodePtr xdr_child, xsd_node;
|
|
xmlChar* str = xmlGetProp(xdr, xs_order);
|
|
xmlAttrPtr xdr_attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr, parent);
|
|
|
|
if (!str || xmlStrEqual(str, xs_seq))
|
|
xsd_node = xmlNewChild(parent, NULL, xs_sequence, NULL);
|
|
else if (xmlStrEqual(str, xs_many))
|
|
xsd_node = xmlNewChild(parent, NULL, xs_choice, NULL);
|
|
else /* one */
|
|
xsd_node = xmlNewChild(parent, NULL, xs_all, NULL);
|
|
xmlFree(str);
|
|
|
|
FOREACH_ATTR(xdr, xdr_attr)
|
|
{
|
|
if (xmlStrEqual(xdr_attr->name, xs_order))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_attr->name, xs_model))
|
|
; /* ignored */
|
|
else if (xmlStrEqual(xdr_attr->name, xs_maxOccurs))
|
|
XDR_A_maxOccurs(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_minOccurs))
|
|
XDR_A_minOccurs(xdr_attr, xsd_node);
|
|
else
|
|
xmlCopyProp(xsd_node, xdr_attr);
|
|
}
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_description))
|
|
XDR_E_description(xdr_child, xsd_node);
|
|
else if (xmlStrEqual(xdr_child->name, xs_element))
|
|
XDR_E_element(xdr_child, xsd_node);
|
|
}
|
|
|
|
return xsd_node;
|
|
}
|
|
|
|
static xmlNodePtr XDR_E_ElementType(xmlNodePtr xdr, xmlNodePtr parent)
|
|
{
|
|
xmlChar *str, *type = get_dt_type(xdr);
|
|
BOOL is_open = TRUE;
|
|
int n_attributes = 0, n_elements = 0, n_groups = 0;
|
|
CONTENT_TYPE content;
|
|
ORDER_TYPE order;
|
|
xmlNodePtr xsd_node, xsd_type, xsd_child, xdr_child;
|
|
xmlAttrPtr xdr_attr;
|
|
xmlNsPtr dt_ns = get_dt_ns(parent);
|
|
|
|
TRACE("(%p, %p)\n", xdr, parent);
|
|
|
|
str = xmlGetProp(xdr, xs_model);
|
|
if (str && !xmlStrEqual(str, xs_open))
|
|
is_open = FALSE;
|
|
xmlFree(str);
|
|
|
|
if (type)
|
|
{
|
|
content = CONTENT_TEXTONLY;
|
|
}
|
|
else
|
|
{
|
|
str = xmlGetProp(xdr, xs_content);
|
|
if (!str || xmlStrEqual(str, xs_mixed))
|
|
content = CONTENT_MIXED;
|
|
else if (xmlStrEqual(str, xs_eltOnly))
|
|
content = CONTENT_ELTONLY;
|
|
else if (xmlStrEqual(str, xs_textOnly))
|
|
content = CONTENT_TEXTONLY;
|
|
else /* empty */
|
|
content = CONTENT_EMPTY;
|
|
xmlFree(str);
|
|
}
|
|
|
|
str = xmlGetProp(xdr, xs_order);
|
|
if (!str || xmlStrEqual(str, xs_seq))
|
|
{
|
|
order = ORDER_SEQ;
|
|
}
|
|
else if (xmlStrEqual(str, xs_many))
|
|
{
|
|
order = ORDER_MANY;
|
|
}
|
|
else /* one */
|
|
{
|
|
order = ORDER_ONE;
|
|
is_open = FALSE;
|
|
}
|
|
xmlFree(str);
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_element))
|
|
++n_elements;
|
|
else if (xmlStrEqual(xdr_child->name, xs_group))
|
|
++n_groups;
|
|
else if (xmlStrEqual(xdr_child->name, xs_attribute))
|
|
++n_attributes;
|
|
}
|
|
|
|
xsd_node = xmlNewChild(parent, NULL, xs_element, NULL);
|
|
assert(xsd_node != NULL);
|
|
switch (content)
|
|
{
|
|
case CONTENT_MIXED:
|
|
case CONTENT_ELTONLY:
|
|
{
|
|
xmlNodePtr xsd_base;
|
|
xsd_type = xmlNewChild(xsd_node, NULL, xs_complexType, NULL);
|
|
|
|
if (content == CONTENT_MIXED)
|
|
xmlSetProp(xsd_type, xs_mixed, xs_true);
|
|
|
|
if (is_open)
|
|
xsd_base = xmlNewChild(xsd_type, NULL, xs_sequence, NULL);
|
|
else
|
|
xsd_base = xsd_type;
|
|
|
|
if (is_open && n_elements < 2 && !n_groups)
|
|
{/* no specific sequence of elements we need,
|
|
just has to start with the right one, if any */
|
|
if ((xdr_child = get_child(xdr, xs_element)))
|
|
{
|
|
xsd_child = XDR_E_element(xdr_child, xsd_base);
|
|
xmlUnsetProp(xsd_child, xs_maxOccurs);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (order)
|
|
{
|
|
case ORDER_SEQ:
|
|
xsd_child = xmlNewChild(xsd_base, NULL, xs_sequence, NULL);
|
|
break;
|
|
case ORDER_MANY:
|
|
xsd_child = xmlNewChild(xsd_base, NULL, xs_choice, NULL);
|
|
xmlSetProp(xsd_child, xs_maxOccurs, xs_unbounded);
|
|
break;
|
|
case ORDER_ONE:
|
|
xsd_child = xmlNewChild(xsd_base, NULL, xs_all, NULL);
|
|
break;
|
|
}
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_element))
|
|
XDR_E_element(xdr_child, xsd_child);
|
|
else if (xmlStrEqual(xdr_child->name, xs_group))
|
|
XDR_E_group(xdr_child, xsd_child);
|
|
}
|
|
}
|
|
|
|
if (n_attributes)
|
|
{
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_attribute))
|
|
XDR_E_attribute(xdr_child, xsd_type);
|
|
}
|
|
}
|
|
|
|
if (is_open)
|
|
{
|
|
add_any_child(xsd_base, TRUE);
|
|
add_anyAttribute_child(xsd_type);
|
|
}
|
|
}
|
|
break;
|
|
case CONTENT_TEXTONLY:
|
|
{
|
|
if (is_open)
|
|
{
|
|
xsd_type = xmlNewChild(xsd_node, NULL, xs_complexType, NULL);
|
|
if (type)
|
|
{
|
|
xsd_child = xmlNewChild(xsd_type, NULL, xs_simpleContent, NULL);
|
|
xsd_child = xmlNewChild(xsd_child, NULL, xs_extension, NULL);
|
|
str = xmlStrdup(DT_prefix);
|
|
str = xmlStrcat(str, BAD_CAST ":");
|
|
str = xmlStrcat(str, type);
|
|
xmlSetProp(xsd_child, xs_base, str);
|
|
xmlFree(str);
|
|
assert(dt_ns != NULL);
|
|
xmlSetNsProp(xsd_node, dt_ns, DT_prefix, type);
|
|
}
|
|
else
|
|
{
|
|
xmlSetProp(xsd_type, xs_mixed, xs_true);
|
|
xsd_child = xmlNewChild(xsd_type, NULL, xs_choice, NULL);
|
|
xmlSetProp(xsd_child, xs_minOccurs, BAD_CAST "0");
|
|
xmlSetProp(xsd_child, xs_maxOccurs, xs_unbounded);
|
|
xsd_child = add_any_child(xsd_child, FALSE);
|
|
xmlSetProp(xsd_child, xs_namespace, BAD_CAST "##other");
|
|
xsd_child = xsd_type;
|
|
}
|
|
|
|
if (n_attributes)
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_attribute))
|
|
XDR_E_attribute(xdr_child, xsd_child);
|
|
}
|
|
|
|
xmlNewChild(xsd_child, NULL, xs_anyAttribute, NULL);
|
|
}
|
|
else if (!n_attributes)
|
|
{
|
|
if (type)
|
|
{
|
|
str = xmlStrdup(DT_prefix);
|
|
str = xmlStrcat(str, BAD_CAST ":");
|
|
str = xmlStrcat(str, type);
|
|
xmlSetProp(xsd_node, xs_type, str);
|
|
xmlFree(str);
|
|
str = NULL;
|
|
xmlSetNsProp(xsd_node, dt_ns, DT_prefix, type);
|
|
}
|
|
else
|
|
{
|
|
xmlSetProp(xsd_node, xs_type, xs_xsd_string);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xsd_type = xmlNewChild(xsd_node, NULL, xs_complexType, NULL);
|
|
xsd_child = xmlNewChild(xsd_type, NULL, xs_simpleContent, NULL);
|
|
xsd_child = xmlNewChild(xsd_child, NULL, xs_extension, NULL);
|
|
xmlSetProp(xsd_child, xs_base, xs_xsd_string);
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_attribute))
|
|
XDR_E_attribute(xdr_child, xsd_child);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CONTENT_EMPTY: /* not allowed with model="open" */
|
|
{
|
|
if (n_attributes)
|
|
{
|
|
xsd_type = xmlNewChild(xsd_node, NULL, xs_complexType, NULL);
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_attribute))
|
|
XDR_E_attribute(xdr_child, xsd_type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xsd_type = xmlNewChild(xsd_node, NULL, xs_simpleType, NULL);
|
|
xsd_child = xmlNewChild(xsd_type, NULL, xs_restriction, NULL);
|
|
xmlSetProp(xsd_child, xs_base, xs_xsd_string);
|
|
xsd_child = xmlNewChild(xsd_child, NULL, xs_length, NULL);
|
|
xmlSetProp(xsd_child, xs_value, BAD_CAST "0");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
xmlFree(type);
|
|
|
|
FOREACH_ATTR(xdr, xdr_attr)
|
|
{
|
|
if (xmlStrEqual(xdr_attr->name, xs_content))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_attr->name, xs_name))
|
|
XDR_A_name(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_type) && xdr_attr->ns == get_dt_ns(xdr))
|
|
XDR_A_dt_type(xdr_attr, xsd_node);
|
|
else if (xmlStrEqual(xdr_attr->name, xs_model))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_attr->name, xs_order))
|
|
; /* already handled */
|
|
else
|
|
xmlCopyProp(xsd_node, xdr_attr);
|
|
|
|
}
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_attribute))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_child->name, xs_AttributeType))
|
|
; /* handled through XDR_E_attribute when parent is not <Schema> */
|
|
else if (xmlStrEqual(xdr_child->name, xs_datatype))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_child->name, xs_description))
|
|
XDR_E_description(xdr_child, xsd_node);
|
|
else if (xmlStrEqual(xdr_child->name, xs_element))
|
|
; /* already handled */
|
|
else if (xmlStrEqual(xdr_child->name, xs_group))
|
|
; /* already handled */
|
|
else
|
|
FIXME("unexpected child <%s>\n", xdr_child->name);
|
|
}
|
|
|
|
return xsd_node;
|
|
}
|
|
|
|
static xmlNodePtr XDR_E_Schema(xmlNodePtr xdr, xmlNodePtr parent, xmlChar const* nsURI)
|
|
{
|
|
xmlNodePtr xsd_node, xdr_child;
|
|
xmlNsPtr ns, xdr_ns;
|
|
xmlAttrPtr xdr_attr;
|
|
|
|
TRACE("(%p, %p)\n", xdr, parent);
|
|
|
|
xsd_node = xmlNewDocNode((xmlDocPtr)parent, NULL, xs_schema, NULL);
|
|
xmlDocSetRootElement((xmlDocPtr)parent, xsd_node);
|
|
assert(xsd_node != NULL);
|
|
|
|
if (nsURI && *nsURI) xmlNewNs(xsd_node, nsURI, NULL);
|
|
ns = xmlNewNs(xsd_node, XSD_href, XSD_prefix);
|
|
assert(ns != NULL);
|
|
|
|
xmlSetNs(xsd_node, ns);
|
|
|
|
if (nsURI && *nsURI) xmlSetProp(xsd_node, xs_targetNamespace, nsURI);
|
|
|
|
FOREACH_NS(xdr, xdr_ns)
|
|
{
|
|
/* TODO: special handling for dt namespace? */
|
|
assert(xdr_ns->href != NULL);
|
|
if (xmlStrEqual(xdr_ns->href, XDR_href))
|
|
; /* ignored */
|
|
else if (xdr_ns->prefix != NULL)
|
|
xmlNewNs(xsd_node, xdr_ns->href, xdr_ns->prefix);
|
|
else
|
|
FIXME("unexpected default xmlns: %s\n", xdr_ns->href);
|
|
}
|
|
|
|
FOREACH_ATTR(xdr, xdr_attr)
|
|
{
|
|
xmlCopyProp(xsd_node, xdr_attr);
|
|
}
|
|
|
|
FOREACH_CHILD(xdr, xdr_child)
|
|
{
|
|
if (xmlStrEqual(xdr_child->name, xs_AttributeType))
|
|
XDR_E_AttributeType(xdr_child, xsd_node);
|
|
else if (xmlStrEqual(xdr_child->name, xs_description))
|
|
XDR_E_description(xdr_child, xsd_node);
|
|
else if (xmlStrEqual(xdr_child->name, xs_ElementType))
|
|
XDR_E_ElementType(xdr_child, xsd_node);
|
|
else
|
|
FIXME("unexpected child <%s>\n", xdr_child->name);
|
|
}
|
|
|
|
return xsd_node;
|
|
}
|
|
|
|
xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI)
|
|
{
|
|
xmlDocPtr xsd_doc = xmlNewDoc(NULL);
|
|
|
|
TRACE("(%p)\n", xdr_doc);
|
|
|
|
XDR_E_Schema(get_schema((xmlNodePtr)xdr_doc), (xmlNodePtr)xsd_doc, nsURI);
|
|
|
|
return xsd_doc;
|
|
}
|
|
|
|
#endif /* HAVE_LIBXML2 */
|