#!/usr/bin/env python
# -*- coding: utf-8 -*-

# parse_xsd.py
#

"""
Experimental code to parse XML Schema files for XML object binding classes
"""

import os
import sys

from lxml import etree

thisdir, thisfilename = os.path.split(__file__)

nsmap = {
    'xsd': 'http://www.w3.org/2001/XMLSchema',
    'p':   'http://purl.oclc.org/ooxml/presentationml/main',
    'a':   'http://purl.oclc.org/ooxml/drawingml/main',
    'r':   'http://purl.oclc.org/ooxml/officeDocument/relationships',
    's':   'http://purl.oclc.org/ooxml/officeDocument/sharedTypes',
}

reverse_nsmap = {uri: prefix for prefix, uri in nsmap.items()}


def pfxdtag(tag):
    """
    Return short-form prefixed tag from fully qualified (Clark notation)
    tagname.
    """
    uri, tagroot = tag[1:].split('}')
    prefix = reverse_nsmap[uri]
    return '%s:%s' % (prefix, tagroot)


def qtag(tag):
    """
    Return fully qualified (Clark notation) tagname corresponding to
    short-form prefixed tagname *tag*.
    """
    prefix, tagroot = tag.split(':')
    uri = nsmap[prefix]
    return '{%s}%s' % (uri, tagroot)


# ============================================================================
# XSD Classes
# ============================================================================

class XsdTree(object):
    """
    Wrapper for ElementTree of a .xsd file
    """
    def __init__(self, xsd_tree, nsprefix):
        super(XsdTree, self).__init__()
        self._tree = xsd_tree
        self.nsprefix = nsprefix

    def xpath(self, xpath):
        return self._tree.xpath(xpath, namespaces=nsmap)


class TypeGraph(object):
    """
    Container for graph of XSD types
    """
    def __init__(self, xsd_trees):
        super(TypeGraph, self).__init__()
        self.__xsd_trees = [XsdTree(tree, nspfx) for tree, nspfx in xsd_trees]
        self.types = {}
        self.elements = {}

    def add_element(self, element):
        """Add *element* to elements dictionary, keyed by name"""
        self.elements[element.tag] = element

    def get_complexType(self, typename):
        """Return complex type element with name *typename*"""
        if typename.startswith('a:'):
            typename = typename[2:]
        xpath = "./xsd:complexType[@name='%s']" % typename
        for xsd in self.__xsd_trees:
            elms = xsd.xpath(xpath)
            if len(elms):
                return elms[0], xsd.nsprefix
        raise KeyError("no complexType named '%s' found" % typename)

    def getdef(self, defname, tag='*'):
        """Return definition element with name *defname*"""
        if defname.startswith('a:'):
            defname = defname[2:]
        for xsd in self.__xsd_trees:
            xpath = "./%s[@name='%s']" % (tag, defname)
            elements = xsd.xpath(xpath)
            if elements:
                return elements[0]
        raise KeyError("no definition named '%s' found" % defname)

    def load_schema(self, xsd_root, nsprefix):
        for elm in xsd_root:
            tag = pfxdtag(elm.tag)
            if tag in ('xsd:import', 'xsd:simpleType', 'xsd:group',
                       'xsd:attributeGroup'):
                continue
            elif tag == 'xsd:complexType':
                ct = ComplexType(self, elm, nsprefix)
                self.types[ct.name] = ct
            elif tag == 'xsd:element':
                # if <xsd:element> has a 'ref' attribute, it is a reference
                # to a global xsd:element
                ref = elm.get('ref')
                if ref is not None:
                    elm = self.getdef(ref, tag='xsd:element')
                self.add_element(Element(elm, nsprefix))
            else:
                raise TypeError("don't know how to process %s" % tag)

    def load_subgraph(self, typename, visited=None):
        complexType_elm, nsprefix = self.get_complexType(typename)
        # take care to avoid infinite recursion by only visting each type once
        visited = [] if visited is None else visited
        if typename in visited:
            return
        visited.append(typename)
        # add type to graph
        complexType = ComplexType(self, complexType_elm, nsprefix)
        self.types[complexType.name] = complexType
        # recurse on subgraph of type
        for element in complexType.elements:
            if element.typename in ('xsd:string'):
                continue
            self.load_subgraph(element.typename, visited)


class ComplexType(object):
    """
    <xsd:complexType> object
    """
    def __init__(self, type_graph, complexType_elm, nsprefix):
        super(ComplexType, self).__init__()
        self.type_graph = type_graph
        self.nsprefix = nsprefix
        self.name = complexType_elm.get('name')
        self.elements = []
        self.attributes = []

        for child_elm in complexType_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:sequence':
                self.__process_sequence(child_elm)
            elif tag == 'xsd:choice':
                self.__expand_choice(child_elm)
            elif tag == 'xsd:group':
                ref = child_elm.get('ref')
                if ref is not None:  # reference to separate group
                    child_elm = self.type_graph.getdef(ref)
                self.__expand_group(child_elm)
            elif tag == 'xsd:attribute':
                self.attributes.append(Attribute(child_elm))
            elif tag == 'xsd:attributeGroup':
                # e.g. ``<xsd:attributeGroup ref="AG_Locking"/>``
                ref = child_elm.get('ref')
                if ref is not None:  # reference to separate group
                    child_elm = self.type_graph.getdef(ref)
                self.__expand_attributeGroup(child_elm)
            else:
                xml = etree.tostring(child_elm)
                raise TypeError("don't know how to process %s" % xml)

    def __str__(self):
        s = '%s' % self.name
        for element in self.elements:
            s += '\n  %s' % element
        for attribute in self.attributes:
            s += '\n  %s' % attribute
        return s

    def add_element(self, element):
        """
        Add an element to this ComplexType and also append it to element dict
        of parent type graph.
        """
        self.elements.append(element)
        self.type_graph.add_element(element)

    @property
    def element_def(self):
        """
        blipFill = ElementDef('p:blipFill', 'CT_BlipFillProperties')
        blipFill.add_child('a:blip', cardinality='?')
        blipFill.add_attributes('dpi', 'rotWithShape')
        """
        s = ("%s = ElementDef('%s', '%s')\n" %
             (self.name, self.name, self.name))
        for element in self.elements:
            s += ("%s.add_child('%s', cardinality='%s')\n" %
                  (self.name, element.name, element.cardinality))
        # for attribute in self.attributes:
        #     s += '\n  %s' % attribute
        return s

    @property
    def max_tagname_len(self):
        """Return length of longest child element tagname"""
        return max([len(e.name) for e in self.elements])

    @property
    def optional_attributes(self):
        """Return list of optional attributes for this type"""
        return [a for a in self.attributes if not a.is_required]

    @property
    def required_attributes(self):
        """Return list of required attributes for this type"""
        return [a for a in self.attributes if a.is_required]

    def __expand_attributeGroup(self, attrgrp_elm):
        for child_elm in attrgrp_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:attribute':
                self.attributes.append(Attribute(child_elm))
            else:
                tmpl = "don't know how to process %s in %s"
                raise TypeError(tmpl % (tag, 'xsd:attributeGroup'))

    def __expand_choice(self, choice_elm):
        for child_elm in choice_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:element':
                element = Element(child_elm, self.nsprefix)
                element.minOccurs = '0'
                self.add_element(element)
            else:
                tmpl = "don't know how to process %s in %s"
                raise TypeError(tmpl % (tag, 'xsd:attributeGroup'))

    def __expand_group(self, group_elm):
        for child_elm in group_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:sequence':
                self.__process_sequence(child_elm)
            elif tag == 'xsd:choice':
                self.__expand_choice(child_elm)
            else:
                print etree.tostring(child_elm)
                tmpl = "don't know how to process %s in %s"
                raise TypeError(tmpl % (tag, 'xsd:group'))

    def __process_sequence(self, seq_elm):
        for child_elm in seq_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:element':
                ref = child_elm.get('ref')
                if ref is not None:  # reference to global xsd:element
                    child_elm = self.type_graph.getdef(ref, tag='xsd:element')
                self.add_element(Element(child_elm, self.nsprefix))
            elif tag == 'xsd:group':
                ref = child_elm.get('ref')
                if ref is not None:  # reference to separate group
                    child_elm = self.type_graph.getdef(ref)
                self.__expand_group(child_elm)
            elif tag == 'xsd:choice':
                self.__expand_choice(child_elm)
            # ignore xsd:any elements, just placeholders for schema expansion
            elif tag in ('xsd:any'):
                continue
            else:
                xml = etree.tostring(child_elm)
                raise TypeError("don't know how to process %s" % xml)


class Attribute(object):
    """
    <xsd:attribute> object
    """
    def __init__(self, attribute_elm):
        super(Attribute, self).__init__()
        self.name = attribute_elm.get('name')
        self.type = attribute_elm.get('type')
        self.use = attribute_elm.get('use', 'optional')
        self.default = attribute_elm.get('default')
        self.ref = attribute_elm.get('ref')
        self.form = attribute_elm.get('form')
        if self.form is not None:
            raise TypeError('found xsd:attribute with form="%s"' % self.form)
        # don't care about details other than name, ref gives that
        if self.name is None:
            self.name = self.ref

    def __str__(self):
        cardinality = '?' if self.use == 'optional' else '1'
        default = (' default="%s"' % self.default) if self.default else ''
        tmpl = 'xsd:attribute %s %s%s'
        return tmpl % (cardinality, self.name, default)

    @property
    def is_required(self):
        return self.use == 'required'


class Element(object):
    """
    <xsd:element> object
    """
    def __init__(self, element_elm, nsprefix):
        super(Element, self).__init__()
        self.nsprefix = nsprefix
        self.name = element_elm.get('name')
        self.tag = '%s:%s' % (self.nsprefix, self.name)
        self.typename = element_elm.get('type')
        if self.typename.startswith('a:'):
            self.typename = self.typename[2:]
        self.default = element_elm.get('default')
        self.form = element_elm.get('form')
        self.minOccurs = element_elm.get('minOccurs', '1')
        self.maxOccurs = element_elm.get('maxOccurs', '1')
        if self.form is not None:
            raise TypeError('found xsd:element with form="%s"' % self.form)

    def __str__(self):
        tmpl = 'xsd:element %s %s   %s%s'
        default = (' default="%s"' % self.default) if self.default else ''
        return tmpl % (self.cardinality, self.name, self.typename, default)

    @property
    def cardinality(self):
        if self.minOccurs == '0':
            cardinality = '?' if self.maxOccurs == '1' else '*'
        elif self.minOccurs == '1':
            cardinality = '1' if self.maxOccurs == '1' else '+'
        else:
            cardinality = '{%s:%s}' % (self.minOccurs, self.maxOccurs)
        return cardinality


def element_defs(tags):
    def _padding(type, element):
        return ' ' * (type.max_tagname_len - len(element.name))

    s = ''
    for tag in tags:
        element = tg.elements[tag]
        # no def needed for built-in types
        if element.typename in ('xsd:string'):
            continue
        try:
            type = tg.types[element.typename]
        except KeyError:
            continue

        # e.g. blipFill = ElementDef('p:blipFill', 'CT_BlipFillProperties')
        s += ("\n%s = ElementDef('%s', '%s')\n" %
              (element.name, element.tag, element.typename))

        # e.g. blipFill.add_child('a:blip', cardinality='?')
        for child in type.elements:
            s += ("%s.add_child('%s'%s, cardinality='%s')\n" %
                  (element.name, child.tag, _padding(type, child),
                   child.cardinality))

        # e.g. ext.add_attribute('x', required=True, default="0")
        for a in type.required_attributes:
            default = a.default if a.default else ''
            s += ("%s.add_attribute('%s', required=True, default='%s')\n" %
                  (element.name, a.name, default))

        # e.g. xfrm.add_attributes('rot', 'flipH', 'flipV')
        if type.optional_attributes:
            params = "', '".join([a.name for a in type.optional_attributes])
            s += "%s.add_attributes('%s')\n" % (element.name, params)

    return s


# ============================================================================
# main
# ============================================================================

pml = etree.parse(os.path.join(thisdir, 'xsd', 'pml.xsd')).getroot()
dml = etree.parse(os.path.join(thisdir, 'xsd', 'dml-main.xsd')).getroot()
sst = etree.parse(os.path.join(thisdir, 'xsd',
                               'shared-commonSimpleTypes.xsd')).getroot()

# xpath = "./xsd:complexType[@name='CT_Shape']"
# ct_shape = pml.xpath(xpath, namespaces=nsmap)[0]

tg = TypeGraph([(pml, 'p'), (dml, 'a'), (sst, 's')])

# tg.load_subgraph('CT_Shape')
# tg.load_subgraph('CT_Slide')
tg.load_schema(pml, 'p')
tg.load_schema(dml, 'a')
tg.load_schema(sst, 's')

# for typename in sorted(tg.types.keys()):
#     print typename
#     # print tg.types[typename]

# print '\n%d distinct types' % len(tg.types)

tags = sorted(tg.elements.keys())

# for tag in tags:
#     print(tag)
#
# print '\n%d distinct tags' % len(tags)

print element_defs(tags)

# tg.load_schema(pml, 'p')
# tg.load_schema(dml, 'a')

# tags = ['p:blipFill', 'p:pic', 'a:blip']
# tags = ['a:off', 'a:ext', 'p:blipFill', 'p:pic', 'a:blip']

# print element_defs(tags)

# BACKLOG: detect namespace prefix automatically from xsd root element

sys.exit()

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# parse_xsd.py
#

"""
Experimental code to parse XML Schema files for XML object binding classes
"""

import os
import sys

from lxml import etree

thisdir, thisfilename = os.path.split(__file__)

nsmap = {
    'xsd': 'http://www.w3.org/2001/XMLSchema',
    'p':   'http://purl.oclc.org/ooxml/presentationml/main',
    'a':   'http://purl.oclc.org/ooxml/drawingml/main',
    'r':   'http://purl.oclc.org/ooxml/officeDocument/relationships',
    's':   'http://purl.oclc.org/ooxml/officeDocument/sharedTypes',
}

reverse_nsmap = {uri: prefix for prefix, uri in nsmap.items()}


def pfxdtag(tag):
    """
    Return short-form prefixed tag from fully qualified (Clark notation)
    tagname.
    """
    uri, tagroot = tag[1:].split('}')
    prefix = reverse_nsmap[uri]
    return '%s:%s' % (prefix, tagroot)


def qtag(tag):
    """
    Return fully qualified (Clark notation) tagname corresponding to
    short-form prefixed tagname *tag*.
    """
    prefix, tagroot = tag.split(':')
    uri = nsmap[prefix]
    return '{%s}%s' % (uri, tagroot)


# ============================================================================
# XSD Classes
# ============================================================================

class XsdTree(object):
    """
    Wrapper for ElementTree of a .xsd file
    """
    def __init__(self, xsd_tree, nsprefix):
        super(XsdTree, self).__init__()
        self._tree = xsd_tree
        self.nsprefix = nsprefix

    def xpath(self, xpath):
        return self._tree.xpath(xpath, namespaces=nsmap)


class TypeGraph(object):
    """
    Container for graph of XSD types
    """
    def __init__(self, xsd_trees):
        super(TypeGraph, self).__init__()
        self.__xsd_trees = [XsdTree(tree, nspfx) for tree, nspfx in xsd_trees]
        self.types = {}
        self.elements = {}

    def add_element(self, element):
        """Add *element* to elements dictionary, keyed by name"""
        self.elements[element.tag] = element

    def get_complexType(self, typename):
        """Return complex type element with name *typename*"""
        if typename.startswith('a:'):
            typename = typename[2:]
        xpath = "./xsd:complexType[@name='%s']" % typename
        for xsd in self.__xsd_trees:
            elms = xsd.xpath(xpath)
            if len(elms):
                return elms[0], xsd.nsprefix
        raise KeyError("no complexType named '%s' found" % typename)

    def getdef(self, defname, tag='*'):
        """Return definition element with name *defname*"""
        if defname.startswith('a:'):
            defname = defname[2:]
        for xsd in self.__xsd_trees:
            xpath = "./%s[@name='%s']" % (tag, defname)
            elements = xsd.xpath(xpath)
            if elements:
                return elements[0]
        raise KeyError("no definition named '%s' found" % defname)

    def load_schema(self, xsd_root, nsprefix):
        for elm in xsd_root:
            tag = pfxdtag(elm.tag)
            if tag in ('xsd:import', 'xsd:simpleType', 'xsd:group',
                       'xsd:attributeGroup'):
                continue
            elif tag == 'xsd:complexType':
                ct = ComplexType(self, elm, nsprefix)
                self.types[ct.name] = ct
            elif tag == 'xsd:element':
                # if <xsd:element> has a 'ref' attribute, it is a reference
                # to a global xsd:element
                ref = elm.get('ref')
                if ref is not None:
                    elm = self.getdef(ref, tag='xsd:element')
                self.add_element(Element(elm, nsprefix))
            else:
                raise TypeError("don't know how to process %s" % tag)

    def load_subgraph(self, typename, visited=None):
        complexType_elm, nsprefix = self.get_complexType(typename)
        # take care to avoid infinite recursion by only visting each type once
        visited = [] if visited is None else visited
        if typename in visited:
            return
        visited.append(typename)
        # add type to graph
        complexType = ComplexType(self, complexType_elm, nsprefix)
        self.types[complexType.name] = complexType
        # recurse on subgraph of type
        for element in complexType.elements:
            if element.typename in ('xsd:string'):
                continue
            self.load_subgraph(element.typename, visited)


class ComplexType(object):
    """
    <xsd:complexType> object
    """
    def __init__(self, type_graph, complexType_elm, nsprefix):
        super(ComplexType, self).__init__()
        self.type_graph = type_graph
        self.nsprefix = nsprefix
        self.name = complexType_elm.get('name')
        self.elements = []
        self.attributes = []

        for child_elm in complexType_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:sequence':
                self.__process_sequence(child_elm)
            elif tag == 'xsd:choice':
                self.__expand_choice(child_elm)
            elif tag == 'xsd:group':
                ref = child_elm.get('ref')
                if ref is not None:  # reference to separate group
                    child_elm = self.type_graph.getdef(ref)
                self.__expand_group(child_elm)
            elif tag == 'xsd:attribute':
                self.attributes.append(Attribute(child_elm))
            elif tag == 'xsd:attributeGroup':
                # e.g. ``<xsd:attributeGroup ref="AG_Locking"/>``
                ref = child_elm.get('ref')
                if ref is not None:  # reference to separate group
                    child_elm = self.type_graph.getdef(ref)
                self.__expand_attributeGroup(child_elm)
            else:
                xml = etree.tostring(child_elm)
                raise TypeError("don't know how to process %s" % xml)

    def __str__(self):
        s = '%s' % self.name
        for element in self.elements:
            s += '\n  %s' % element
        for attribute in self.attributes:
            s += '\n  %s' % attribute
        return s

    def add_element(self, element):
        """
        Add an element to this ComplexType and also append it to element dict
        of parent type graph.
        """
        self.elements.append(element)
        self.type_graph.add_element(element)

    @property
    def element_def(self):
        """
        blipFill = ElementDef('p:blipFill', 'CT_BlipFillProperties')
        blipFill.add_child('a:blip', cardinality='?')
        blipFill.add_attributes('dpi', 'rotWithShape')
        """
        s = ("%s = ElementDef('%s', '%s')\n" %
             (self.name, self.name, self.name))
        for element in self.elements:
            s += ("%s.add_child('%s', cardinality='%s')\n" %
                  (self.name, element.name, element.cardinality))
        # for attribute in self.attributes:
        #     s += '\n  %s' % attribute
        return s

    @property
    def max_tagname_len(self):
        """Return length of longest child element tagname"""
        return max([len(e.name) for e in self.elements])

    @property
    def optional_attributes(self):
        """Return list of optional attributes for this type"""
        return [a for a in self.attributes if not a.is_required]

    @property
    def required_attributes(self):
        """Return list of required attributes for this type"""
        return [a for a in self.attributes if a.is_required]

    def __expand_attributeGroup(self, attrgrp_elm):
        for child_elm in attrgrp_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:attribute':
                self.attributes.append(Attribute(child_elm))
            else:
                tmpl = "don't know how to process %s in %s"
                raise TypeError(tmpl % (tag, 'xsd:attributeGroup'))

    def __expand_choice(self, choice_elm):
        for child_elm in choice_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:element':
                element = Element(child_elm, self.nsprefix)
                element.minOccurs = '0'
                self.add_element(element)
            else:
                tmpl = "don't know how to process %s in %s"
                raise TypeError(tmpl % (tag, 'xsd:attributeGroup'))

    def __expand_group(self, group_elm):
        for child_elm in group_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:sequence':
                self.__process_sequence(child_elm)
            elif tag == 'xsd:choice':
                self.__expand_choice(child_elm)
            else:
                print etree.tostring(child_elm)
                tmpl = "don't know how to process %s in %s"
                raise TypeError(tmpl % (tag, 'xsd:group'))

    def __process_sequence(self, seq_elm):
        for child_elm in seq_elm:
            tag = pfxdtag(child_elm.tag)
            if tag == 'xsd:element':
                ref = child_elm.get('ref')
                if ref is not None:  # reference to global xsd:element
                    child_elm = self.type_graph.getdef(ref, tag='xsd:element')
                self.add_element(Element(child_elm, self.nsprefix))
            elif tag == 'xsd:group':
                ref = child_elm.get('ref')
                if ref is not None:  # reference to separate group
                    child_elm = self.type_graph.getdef(ref)
                self.__expand_group(child_elm)
            elif tag == 'xsd:choice':
                self.__expand_choice(child_elm)
            # ignore xsd:any elements, just placeholders for schema expansion
            elif tag in ('xsd:any'):
                continue
            else:
                xml = etree.tostring(child_elm)
                raise TypeError("don't know how to process %s" % xml)


class Attribute(object):
    """
    <xsd:attribute> object
    """
    def __init__(self, attribute_elm):
        super(Attribute, self).__init__()
        self.name = attribute_elm.get('name')
        self.type = attribute_elm.get('type')
        self.use = attribute_elm.get('use', 'optional')
        self.default = attribute_elm.get('default')
        self.ref = attribute_elm.get('ref')
        self.form = attribute_elm.get('form')
        if self.form is not None:
            raise TypeError('found xsd:attribute with form="%s"' % self.form)
        # don't care about details other than name, ref gives that
        if self.name is None:
            self.name = self.ref

    def __str__(self):
        cardinality = '?' if self.use == 'optional' else '1'
        default = (' default="%s"' % self.default) if self.default else ''
        tmpl = 'xsd:attribute %s %s%s'
        return tmpl % (cardinality, self.name, default)

    @property
    def is_required(self):
        return self.use == 'required'


class Element(object):
    """
    <xsd:element> object
    """
    def __init__(self, element_elm, nsprefix):
        super(Element, self).__init__()
        self.nsprefix = nsprefix
        self.name = element_elm.get('name')
        self.tag = '%s:%s' % (self.nsprefix, self.name)
        self.typename = element_elm.get('type')
        if self.typename.startswith('a:'):
            self.typename = self.typename[2:]
        self.default = element_elm.get('default')
        self.form = element_elm.get('form')
        self.minOccurs = element_elm.get('minOccurs', '1')
        self.maxOccurs = element_elm.get('maxOccurs', '1')
        if self.form is not None:
            raise TypeError('found xsd:element with form="%s"' % self.form)

    def __str__(self):
        tmpl = 'xsd:element %s %s   %s%s'
        default = (' default="%s"' % self.default) if self.default else ''
        return tmpl % (self.cardinality, self.name, self.typename, default)

    @property
    def cardinality(self):
        if self.minOccurs == '0':
            cardinality = '?' if self.maxOccurs == '1' else '*'
        elif self.minOccurs == '1':
            cardinality = '1' if self.maxOccurs == '1' else '+'
        else:
            cardinality = '{%s:%s}' % (self.minOccurs, self.maxOccurs)
        return cardinality


def element_defs(tags):
    def _padding(type, element):
        return ' ' * (type.max_tagname_len - len(element.name))

    s = ''
    for tag in tags:
        element = tg.elements[tag]
        # no def needed for built-in types
        if element.typename in ('xsd:string'):
            continue
        try:
            type = tg.types[element.typename]
        except KeyError:
            continue

        # e.g. blipFill = ElementDef('p:blipFill', 'CT_BlipFillProperties')
        s += ("\n%s = ElementDef('%s', '%s')\n" %
              (element.name, element.tag, element.typename))

        # e.g. blipFill.add_child('a:blip', cardinality='?')
        for child in type.elements:
            s += ("%s.add_child('%s'%s, cardinality='%s')\n" %
                  (element.name, child.tag, _padding(type, child),
                   child.cardinality))

        # e.g. ext.add_attribute('x', required=True, default="0")
        for a in type.required_attributes:
            default = a.default if a.default else ''
            s += ("%s.add_attribute('%s', required=True, default='%s')\n" %
                  (element.name, a.name, default))

        # e.g. xfrm.add_attributes('rot', 'flipH', 'flipV')
        if type.optional_attributes:
            params = "', '".join([a.name for a in type.optional_attributes])
            s += "%s.add_attributes('%s')\n" % (element.name, params)

    return s


# ============================================================================
# main
# ============================================================================

pml = etree.parse(os.path.join(thisdir, 'xsd', 'pml.xsd')).getroot()
dml = etree.parse(os.path.join(thisdir, 'xsd', 'dml-main.xsd')).getroot()
sst = etree.parse(os.path.join(thisdir, 'xsd',
                               'shared-commonSimpleTypes.xsd')).getroot()

# xpath = "./xsd:complexType[@name='CT_Shape']"
# ct_shape = pml.xpath(xpath, namespaces=nsmap)[0]

tg = TypeGraph([(pml, 'p'), (dml, 'a'), (sst, 's')])

# tg.load_subgraph('CT_Shape')
# tg.load_subgraph('CT_Slide')
tg.load_schema(pml, 'p')
tg.load_schema(dml, 'a')
tg.load_schema(sst, 's')

# for typename in sorted(tg.types.keys()):
#     print typename
#     # print tg.types[typename]

# print '\n%d distinct types' % len(tg.types)

tags = sorted(tg.elements.keys())

# for tag in tags:
#     print(tag)
#
# print '\n%d distinct tags' % len(tags)

print element_defs(tags)

# tg.load_schema(pml, 'p')
# tg.load_schema(dml, 'a')

# tags = ['p:blipFill', 'p:pic', 'a:blip']
# tags = ['a:off', 'a:ext', 'p:blipFill', 'p:pic', 'a:blip']

# print element_defs(tags)

# BACKLOG: detect namespace prefix automatically from xsd root element

sys.exit()

#!/usr/bin/env python # -*- coding: utf-8 -*- # parse_xsd.py # Experimental code to parse XML Schema files for XML object binding classes Return short-form prefixed tag from fully qualified (Clark notation)
     tagname. Return fully qualified (Clark notation) tagname corresponding to
     short-form prefixed tagname *tag*. # ============================================================================ # XSD Classes # ============================================================================ Wrapper for ElementTree of a .xsd file Container for graph of XSD types Add *element* to elements dictionary, keyed by name Return complex type element with name *typename* Return definition element with name *defname* # if <xsd:element> has a 'ref' attribute, it is a reference # to a global xsd:element # take care to avoid infinite recursion by only visting each type once # add type to graph # recurse on subgraph of type <xsd:complexType> object # reference to separate group # e.g. ``<xsd:attributeGroup ref="AG_Locking"/>`` # reference to separate group Add an element to this ComplexType and also append it to element dict
         of parent type graph. blipFill = ElementDef('p:blipFill', 'CT_BlipFillProperties')
         blipFill.add_child('a:blip', cardinality='?')
         blipFill.add_attributes('dpi', 'rotWithShape') # for attribute in self.attributes: #     s += '\n  %s' % attribute Return length of longest child element tagname Return list of optional attributes for this type Return list of required attributes for this type # reference to global xsd:element # reference to separate group # ignore xsd:any elements, just placeholders for schema expansion <xsd:attribute> object # don't care about details other than name, ref gives that <xsd:element> object # no def needed for built-in types # e.g. blipFill = ElementDef('p:blipFill', 'CT_BlipFillProperties') # e.g. blipFill.add_child('a:blip', cardinality='?') # e.g. ext.add_attribute('x', required=True, default="0") # e.g. xfrm.add_attributes('rot', 'flipH', 'flipV') # ============================================================================ # main # ============================================================================ # xpath = "./xsd:complexType[@name='CT_Shape']" # ct_shape = pml.xpath(xpath, namespaces=nsmap)[0] # tg.load_subgraph('CT_Shape') # tg.load_subgraph('CT_Slide') # for typename in sorted(tg.types.keys()): #     print typename #     # print tg.types[typename] # print '\n%d distinct types' % len(tg.types) # for tag in tags: #     print(tag) # # print '\n%d distinct tags' % len(tags) # tg.load_schema(pml, 'p') # tg.load_schema(dml, 'a') # tags = ['p:blipFill', 'p:pic', 'a:blip'] # tags = ['a:off', 'a:ext', 'p:blipFill', 'p:pic', 'a:blip'] # print element_defs(tags) # BACKLOG: detect namespace prefix automatically from xsd root element